aboutsummaryrefslogtreecommitdiffstats
path: root/odl-aaa-moon/aaa/aaa-authn-api/src/main/docs/sssd_configuration.rst
blob: 7f912d94565dcb4578a10437b2add232f06ec876 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
################################################
Federated Authentication Utilizing Apache & SSSD
################################################

:Author: John Dennis
:Email: jdennis@redhat.com

.. contents:: Table of Contents

************
Introduction
************

Applications should not need to handle the burden of authentication
and authorization. These are complex technologies further complicated
by the existence of a wide variety of authentication
mechanisms. Likewise there are numerous identity providers (IdP) which
one may wish to utilize, perhaps in a federated manner. The potential
to make critical mistakes are high while consuming significant
engineering resources. Ideally an application should "outsource" it's
authentication to an "expert" and avoid unnecessary development costs.

For web based applications (both conventional HTML and REST API) there
has been a trend to embed a simple HTTP server in the application or
application server which handles the HTTP requests eschewing the use
of a traditional web server such as Apache.

.. figure:: sssd_01.png
   :align: center

   _`Figure 1.`

But traditional web servers have a lot of advantages. They often come
with extensive support for technologies you might wish to utilize in
your application. It would require signification software engineering
to add support for those technologies in your application. The problem
is compounded by the fact many of these technologies demand domain
expertise which is unlikely to be available in the application
development team. Another problem is the libraries needed to utilize
the technology may not even be available in the programming language
the application is being developed in. Fundamentally an application
developer should focus on developing their application instead of
investing resources into implementing complex code for the ancillary
technologies the application may wish to utilize.

Therefore fronting your application with a web server such as Apache
makes a lot of sense. One should allow Apache to handle complex tasks
such as multiple authentication mechanisms talking to multiple
IdP's. Suppose you want your application to handle Single Sign-On
(SSO) via Kerberos or authentication based on X509 certificates
(i.e. PKI). Apache already has extensions to handle these which have
been field proven, it would be silly to try and support these in your
application. Apache also comes with other useful extensions such as
``mod_identity_lookup`` which can extract metadata about an
authenticated user from multiple sources such as LDAP,
Active Directory, NIS, etc.

By fronting your application with Apache and allowing Apache to handle
the complex task of authentication, identity lookups etc. you've
greatly increased the features of your application while at the same
time reducing application development time along with increasing
application security and robustness.

.. figure:: sssd_02.png
   :align: center

   _`Figure 2.`

When Apache fronts your application you will be passed the results of
authentication and identity lookups. Your application only needs a
simple mechanism to accept these values. There are a variety of ways
the values can be passed from Apache to your application which will be
discussed in later sections.

Authentication & Identity Properties
====================================

Authentication is proving that a user is who they claim to be, in
other words after authentication the user has a proven identity. In
security parlance the authenticated entity is call a
principal. Principals may be humans, machines or
services. Authorization is distinct from authentication. Authorization
declares what actions an authenticated principal may perform. For
example, does a principal have permission to read a certain file, run
a specific command, etc. Identity metadata is typically bound to the
principal to provide extra information. Examples include the users
full name, their organization, the groups they are members of, etc.

Apache can provide both authentication and identity metadata to an
application freeing the application of this task. Authorization
usually will remain the province of the application. A typical
design pattern is to assign roles to a principal based on identity
properties. As the application executes on behalf of a principal the
application will check if the principal has the necessary role needed
to perform the operation.

Apache ships with a wide variety of authentication modules. After an
Apache authentication module successfully authenticates a principal, it
sets internal variables identifying the principal and the
authentication method used to authenticate the principal. These are
exported as the CGI variables REMOTE_USER and AUTH_TYPE respectively
(see `CGI Export Issues`_ for further information).

Identity Properties
-------------------

Most Apache authentication modules do not have access to any of the
identity properties bound to the authenticated principal. Those
identity properties must be provided by some other mechanism. Typical
mechanisms include lookups in LDAP, Active Directory, NIS, POSIX
passwd/gecos and SQL. Managing these lookups can be difficult
especially in a networked environment where services may be
temporarily unavailable and/or in a enterprise deployment where
identity sources must be multiplexed across a variety of services
according to enterprise wide policy.

`SSSD`_ (System Security Services Daemon) is designed to alleviate many
of the problems surrounding authentication and identity property
lookup. SSSD can provide identity properties via D-Bus using it's
InfoPipe (IFP) feature. The `mod_identity_lookup`_ Apache module is
given the name of the authenticated principal and makes available
identity properties via Apache environment variables (see `Configure
SSSD IFP`_ for details).

Exporting & Consuming Identity Metadata
=======================================

The authenticated principal (REMOTE_USER), the mechanism used to
authenticate the principal (AUTH_TYPE) and identity properties
(supplied by SSSD IFP) are exported to the application which trusts
this metadata to be valid.

How is this identity metadata exported from Apache and then be
consumed by a Java EE Servlet?

The architectural design inside Apache tries to capitalize on the
existing CGI standard (`CGI RFC`_) as much as possible. CGI defines
these relevant environment variables:

  * REMOTE_USER
  * AUTH_TYPE
  * REMOTE_ADDR
  * REMOTE_HOST


Transporting Identity Metadata from Apache to a Java EE Servlet
===============================================================

In following figure we can see that the user connects to Apache
instead of the servlet container. Apache authenticates the user, looks
up the principal's identity information and then proxies the request
to the servlet container. The additional identity metadata must be
included in the proxy request in order for the servlet to extract it.

.. figure:: sssd_03.png
   :align: center

   _`Figure 3.`

The Java EE Servlet API is designed with the HTTP protocol in mind
however the servlet never directly accesses the HTTP protocol stream.
Instead it uses the servlet API to get access to HTTP request
data. The responsibility for HTTP communication rests with the
container's ``Connector`` objects. When the servlet API needs
information it works in conjunction with the ``Connector`` to supply
it. For example the ``HttpServletRequest.getRemoteHost()`` method
interrogates information the ``Connector`` placed on the internal
request object. Analogously ``HttpServletRequest.getRemoteUser()``
interrogates information placed on the internal request object by an
authentication filter.

But what happens when a HTTP request is proxied to a servlet container
by Apache and ``getRemoteHost()`` or ``getRemoteUser()`` is called? Most
``Connector`` objects do not understand the proxy scenario, to them
a request from a proxy looks just like a request sent directly to the
servlet container. Therefore ``getRemoteHost()`` or ``getRemoteUser()``
ends up returning information relative to the proxy instead of the
user who connected to the proxy because it's the proxy who connected
to the servlet container and not the end user. There are 2 fundamental
approaches which allow the servlet API to return data supplied by the
proxy:

  1. Proxy uses special protocol (e.g. AJP) to embed metadata.
  2. Metadata is embedded in an HTTP extension by the proxy (i.e. headers)

Proxy With AJP Protocol
-----------------------

The AJP_ protocol was designed as a protocol to exchange HTTP requests
and responses between Apache and a Java EE Servlet Container. One of
its design goals was to improve performance by translating common text
values appearing in HTTP requests to a more compact binary form. At
the same time AJP provided a mechanism to supply metadata about the
request to the servlet container. That metadata is encoded in an AJP
attribute (a name/value pair). The Apache AJP Proxy module looks up
information in the internal Apache request object (e.g. remote user,
remote address, etc.) and encodes that metadata in AJP attributes. On
the servlet container side a AJP ``Connector`` object is aware of these
metadata attributes, extracts them from the protocol and supplies
their values to the upper layers of the servlet API. Thus a call to
``HttpServletRequest.getRemoteUser()`` made by a servlet will receive
the value set by Apache prior to the proxy. This is the desired and
expected behavior. A servlet should be ignorant of the consequences of
proxies; the servlet API should behave the same regardless of the
presence of a proxy.

The AJP protocol also has a general purpose attribute mechanism whereby
any arbitrary name/value pair can be passed. This proxy metadata can
be retrieved by a servlet by calling ``ServletRequest.getAttribute()``
[1]_ When Apache mod_proxy_ajp is being used the authentication
metadata for the remote user and auth type are are automatically
inserted into the AJP protocol and the AJP ``Connector`` object on
the servlet receiving end supplies those values to
``HttpServletRequest.getRemoteHost()`` and
``HttpServletRequest.getRemoteUser()`` respectively. But the identity
metadata supplied by ``mod_identity_lookup`` needs to be explicitly
encoded into an AJP attribute (see `Configure SSSD IFP`_ for details)
that can later be retrieved by ``ServletRequest.getAttribute()``.

Proxy With HTTP Protocol
------------------------

Although the AJP protocol offers a number of nice advantages sometimes
it's not an option. Not all servlet containers support AJP or there
may be some other deployment constraint that precludes its use. In this
case option 2 from above needs to be used. Option 2 requires only the
defined HTTP protocol be used without any "out of band" metadata. The
conventional way to attach extension metadata to a HTTP request is to
add extension HTTP headers.

One problem with using extension HTTP headers to pass metadata to a
servlet is the expectation the servlet API will have the same
behavior. In other words the value returned by
``HttpServletRequest.getRemoteUser()`` should not depend on whether the
proxy request was exchanged with the AJP protocol or the HTTP
protocol. The solution to this is to wrap the ``HttpServletRequest``
object in a servlet filter. The wrapper overrides certain request
methods (e.g. ``getRemoteUser()``). The override method looks to see if
the metadata is in the extension HTTP headers, if so it returns the
value found in the extension HTTP header otherwise it defers to the
existing servlet implementation. The ``ServletRequest.getAttribute()`` is
overridden in an analogous manner in the wrapper filter. Any call to
``ServletRequest.getAttribute()`` is first checked to see if the value
exists in the extension HTTP header first.

Metadata supplied by Apache that is **not** part of the normal Java
EE Servlet API **always** appears to the servlet via the
``ServletRequest.getAttribute()`` method regardless of the proxy
transport mechanism. The consequence of this is a servlet
continues to utilize the existing Java EE Servlet API without concern
for intermediary proxies, *and* any other metadata supplied by a proxy
is *always* retrieved via ``ServletRequest.getAttribute()`` (see the
caveat about ``ServletRequest.getAttributeNames()`` [1]_).

*******************
Configuration Guide
*******************

Although Apache authentication and SSSD identity lookup can operate
with a variety of authentication mechanisms, IdP's and identity
metadata providers we will demonstrate a configuration example which
utilizes the FreeIPA_ IdP. FreeIPA excels at Kerberos SSO authentication,
Active Directory integration, LDAP based identity metadata storage and
lookup, DNS services, host based RBAC, SSH key management, certificate
management, friendly web based console, command line tools and many
other advanced IdP features.

The following configuration steps will need to be performed:

1. Install FreeIPA_ by following the installation guides in the FreeIPA_
   documentation area. When you install FreeIPA_ you will need to select a
   realm (a.k.a domain) in which your users and hosts will exist. In
   our example we will use the ``EXAMPLE.COM`` realm.

2. Install and configure the Apache HTTP web server. The
   recommendation is to install and run the Apache HTTP web server on
   the same system the Java EE Container running AAA is installed on.

3. Configure the proxy connector in the Java EE Container and set the
   ``secureProxyPorts``.

We will also illustrate the operation of the system by adding an
example user named ``testuser`` who will be a member of the
``odl_users`` and ``odl_admin`` groups.

Add Example User and Groups to FreeIPA
======================================

After installing FreeIPA you will need to populate FreeIPA with your users,
groups and other data. Refer to the documentation in FreeIPA_ for the
variety of ways this task can be performed; it runs the gamut from web
based console to command line utilities. For simplicity we will use
the command line utilities.

Identify yourself to FreeIPA as an administrator; this will give you the
necessary privileges needed to create and modify data in FreeIPA. You do
this by obtaining a Kerberos ticket for the ``admin`` user (or any
other user in FreeIPA with administrator privileges.

::

  % kinit admin@EXAMPLE.COM

Create the example ``odl_users`` and `odl_admin`` groups.

::

  % ipa group-add odl_users --desc 'OpenDaylight Users'
  % ipa group-add odl_admin --desc 'OpenDaylight Administrators'

Create the example user ``testuser`` with the first name "Test" and a
last name of "User" and an email address of "test.user@example.com"

::

  % ipa user-add testuser --first Test --last User --email test.user@example.com

Now add ``testuser`` to the ``odl_users`` and ``odl_admin`` groups.

::

  % ipa group-add-member odl_users --user testuser
  % ipa group-add-member odl_admin --user testuser

Configure Apache
================

A number of Apache configuration directives will need to be specified
to implement the Apache to application binding. Although these
configuration directives can be located in any number of different
Apache configuration files the most sensible approach is to co-locate
them in a single application configuration file. This greatly
simplifies the deployment of your application and isolates your
application configuration from other applications and services sharing
the Apache installation. In the examples that follow our application
will be named ``my_app`` and the Apache application configuration file
will be named ``my_app.conf`` which should be located in Apache's
``conf.d/`` directory. The web resource we are protecting and
supplying identity metadata for will be named ``my_resource``.


Configure Apache for Kerberos
-----------------------------

When FreeIPA is deployed Kerberos is the preferred authentication mechanism
for Single Sign-On (SSO). FreeIPA also provides identity metadata via
Apache ``mod_identity_lookup``. To protect your ``my_resource`` resource
with Kerberos authentication identify your resource as requiring
Kerberos authentication in your ``my_app.conf`` Apache
configuration. For example:

::

  <Location my_resource>
    AuthType Kerberos
    AuthName "Kerberos Login"
    KrbMethodNegotiate On
    KrbMethodK5Passwd Off
    KrbAuthRealms EXAMPLE.COM
    Krb5KeyTab /etc/http.keytab
    require valid-user
  </Location>

You will need to replace EXAMPLE.COM in the KrbAuthRealms declaration
with the Kerberos realm for your deployment.


Configure SSSD IFP
------------------

To use the Apache ``mod_identity_lookup`` module to supply identity
metadata you need to do the following in ``my_app.conf``:

1. Enable the module

   ::

     LoadModule lookup_identity_module modules/mod_lookup_identity.so

2. Apply the identity metadata lookup to specific URL's
   (e.g. ``my_resource``) via an Apache location directive. In this
   example we look up the "mail" attribute and assign it to the
   REMOTE_USER_EMAIL environment variable.

   ::

     <LocationMatch "my_resource">
       LookupUserAttr mail REMOTE_USER_EMAIL
     </LocationMatch>

3. Export the environment variable via the desired proxy protocol, see
   `Exporting Environment Variables to the Proxy`_

Exporting Environment Variables to the Proxy
--------------------------------------------

First you need to decide which proxy protocol you're going to use, AJP
or HTTP and then determine the target address and port to proxy to. The
recommended configuration is to run both the Apache server and the
servlet container on the same host and to proxy requests over the
local loopback interface (see `Declaring the Connector Ports for
Authentication Proxies`_). In our examples we'll use port 8383. Thus
in ``my_app.conf`` add a proxy declaration.

For HTTP Proxy

::

   ProxyPass / http://localhost:8383/
   ProxyPassReverse / http://localhost:8383/

For AJP Proxy

::

   ProxyPass / ajp://localhost:8383/
   ProxyPassReverse / ajp://localhost:8383/

AJP Exports
^^^^^^^^^^^

AJP automatically forwards REMOTE_USER and AUTH_TYPE making them
available to the ``HttpServletRequest`` API, thus you do not need to
explicitly forward these in the proxy configuration. However all other
``mod_identity_lookup`` metadata must be explicitly forwarded as an AJP
attribute. These AJP attributes become visible in the
``ServletRequest.getAttribute()`` method [1]_.

The Apache ``mod_proxy_ajp`` module automatically sends any Apache
environment variable prefixed with "AJP\_" as an AJP attribute which
can be retrieved with ``ServletRequest.getAttribute()``. Therefore the
``mod_identity_lookup`` directives which specify the Apache environment
variable to set with the result of a lookup must be prefixed with
"AJP\_". Using the above example of looking up the principal's email
address we modify the environment variable to include the "AJP\_"
prefix. Thusly:

   ::

     <LocationMatch "my_resource">
       LookupUserAttr mail AJP_REMOTE_USER_EMAIL
     </LocationMatch>

The sequence of events is as follows:

  1. When the URL matches "my_resource".

  2. ``mod_identity_lookup`` retrieves the mail attribute for the
     principal.

  3. ``mod_identity_lookup`` assigns the value of the mail attribute
     lookup to the AJP_REMOTE_USER_EMAIL Apache environment variable.

  4. ``mod_proxy_ajp`` encodes AJP_REMOTE_USER_EMAIL environment
     variable into an AJP attribute in the AJP protocol because the
     environment variable is prefixed with "AJP\_". The name of the
     attribute is stripped of it's "AJP\_" prefix thus the
     AJP_REMOTE_USER_EMAIL environment variable is transferred as the
     AJP attribute REMOTE_USER_EMAIL.

  5. The request is forwarded (i.e. proxied) to servlet container
     using the AJP protocol.

  6. The servlet container's AJP ``Connector`` object is assigned each AJP
     attribute to the set of attributes on the ``ServletRequest``
     attribute list. Thus a call to
     ``ServletRequest.getAttribute("REMOTE_USER_EMAIL")`` yields the
     value set by ``mod_identity_lookup``.


HTTP Exports
^^^^^^^^^^^^

When HTTP proxy is used there are no automatic or implicit metadata
transfers; every metadata attribute must be explicitly handled on both
ends of the proxy connection. All identity metadata attributes are
transferred as extension HTTP headers, by convention those headers are
prefixed with "X-SSSD-".

Using the original example of looking up the principal's email
address we must now perform two independent actions:

  1. Lookup the value via ``mod_identity_lookup`` and assign to an
     Apache environment variable.

  2. Export the environment variable in the request header with the
     "X-SSSD-" prefix.

   ::

     <LocationMatch "my_resource">
       LookupUserAttr mail REMOTE_USER_EMAIL
       RequestHeader set X-SSSD-REMOTE_USER_EMAIL %{REMOTE_USER_EMAIL}e
     </LocationMatch>

The sequence of events is as follows:

  1. When the URL matches "my_resource".

  2. ``mod_identity_lookup`` retrieves the mail attribute for the
     principal.

  3. ``mod_identity_lookup`` assigns the value of the mail attribute
     lookup to the REMOTE_USER_EMAIL Apache environment variable.

  4. Apache's RequestHeader directive executes just prior to the
     request being forwarded (i.e. in the Apache fixup stage). It adds
     the header X-SSSD-REMOTE_USER_EMAIL and assigns the value for
     REMOTE_USER_EMAIL found in the set of environment variables. It
     does this because the syntax %{XXX} is a variable reference for
     the name XXX and the 'e' appended after the closing brace
     indicates the lookup is to be performed in the set of environment
     variables.

  5. The request is forwarded (i.e. proxied) to the servlet container
     using the HTTP protocol.

  6. When ``ServletRequest.getAttribute()`` is called the ``SssdFilter``
     wrapper intercepts the ``getAttribute()`` method. It looks for an
     HTTP header of the same name with "X-SSSD-" prefixed to it. In
     this case ``getAttribute("REMOTE_USER_EMAIL")`` causes the lookup of
     "X-SSSD-REMOTE_USER_EMAIL" in the HTTP headers, if found that
     value is returned.

AJP Proxy Example Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you are using AJP proxy to the Java EE Container on port 8383 your
``my_app.conf`` Apache configuration file will probably look like
this:

::

  <LocationMatch "my_resource">

    ProxyPass / ajp://localhost:8383/
    ProxyPassReverse / ajp://localhost:8383/

    LookupUserAttr mail AJP_REMOTE_USER_EMAIL " "
    LookupUserAttr givenname AJP_REMOTE_USER_FIRSTNAME
    LookupUserAttr sn AJP_REMOTE_USER_LASTNAME
    LookupUserGroups AJP_REMOTE_USER_GROUPS ":"

  </LocationMatch>

Note the specification of the colon separator for the
``LookupUserGroups`` operation. [3]_

HTTP Proxy Example Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you are using a conventional HTTP proxy to the Java EE Container on
port 8383 your ``my_app.conf`` Apache configuration file will probably
look like this:

::

  <LocationMatch "my_resource">

    ProxyPass / http://localhost:8383/
    ProxyPassReverse / http://localhost:8383/

    RequestHeader set X-SSSD-REMOTE_USER expr=%{REMOTE_USER}
    RequestHeader set X-SSSD-AUTH_TYPE expr=%{AUTH_TYPE}
    RequestHeader set X-SSSD-REMOTE_HOST expr=%{REMOTE_HOST}
    RequestHeader set X-SSSD-REMOTE_ADDR expr=%{REMOTE_ADDR}

    LookupUserAttr mail REMOTE_USER_EMAIL
    RequestHeader set X-SSSD-REMOTE_USER_EMAIL %{REMOTE_USER_EMAIL}e

    LookupUserAttr givenname REMOTE_USER_FIRSTNAME
    RequestHeader set X-SSSD-REMOTE_USER_FIRSTNAME %{REMOTE_USER_FIRSTNAME}e

    LookupUserAttr sn REMOTE_USER_LASTNAME
    RequestHeader set X-SSSD-REMOTE_USER_LASTNAME %{REMOTE_USER_LASTNAME}e

    LookupUserGroups REMOTE_USER_GROUPS ":"
    RequestHeader set X-SSSD-REMOTE_USER_GROUPS %{REMOTE_USER_GROUPS}e

  </LocationMatch>

Note the specification of the colon separator for the
``LookupUserGroups`` operation. [3]_


Configure Java EE Container Proxy Connector
===========================================

The Java EE Container must be configured to listen for connections
from the Apache web server. A Java EE Container specifies connections
via a ``Connector`` object. A ``Connector`` **must** be dedicated
**exclusively** for handling authenticated requests from the Apache
web server. The reason for this is explained in `The Proxy
Problem`_. In addition ``ClaimAuthFilter`` needs to validate that any
request it processes originated from the trusted Apache instance. This
is accomplished by dedicating one or more ports exclusively for use by
the trusted Apache server and enumerating them in the
``secureProxyPorts`` configuration as explained in `Locking Down the
Apache to Java EE Container Channel`_ and `Declaring the Connector
Ports for Authentication Proxies`_.

Configure Tomcat Proxy Connector
--------------------------------

The Tomcat Java EE Container defines Connectors in its ``server.xml``
configuration file.

::

    <Connector
        address="127.0.0.1"
        port="8383"
        protocol="HTTP/1.1"
        tomcatAuthentication="false"
        connectionTimeout="20000"
        redirectPort="8443"
    />


:address:
  This should be the loopback address as explained `Locking Down the
  Apache to Java EE Container Channel`_.

:port:
  In our examples we've been using port 8383 as the proxy port. The
  exact port is not important but it must be consistent with the
  Apache proxy port, the ``Connector`` declaration, and the port value
  in ``secureProxyPorts``.

:protocol:
  As explained in `Transporting Identity Metadata from Apache to a
  Java EE Servlet`_ you will need to decide if you are using HTTP or
  AJP as the proxy protocol. In the example above the protocol is set
  for HTTP, if you use AJP instead the protocol should instead be
  "AJP/1.3".

:tomcatAuthentication:
  This boolean flag tells Tomcat whether Tomcat should perform
  authentication on the incoming requests or not. Since authentication
  is performed by Apache we do not want Tomcat to perform
  authentication therefore this flag must be set to false.

The AAA system needs to know which port(s) the trusted Apache proxy
will be sending requests on so it can trust the request authentication
metadata. See `Declaring the Connector Ports for Authentication
Proxies`_ for more information). Set ``secureProxyPorts`` in the
FederationConfiguration.

::

  secureProxyPorts=8383


Configure Jetty Proxy Connector
-------------------------------

The Jetty Java EE Container defines Connectors in its ``jetty.xml``
configuration file.

::

    <!-- Trusted Authentication Federation proxy connection -->
    <Call name="addConnector">
        <Arg>
            <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <Set name="host">127.0.0.1</Set>
                <Set name="port">8383</Set>
                <Set name="maxIdleTime">300000</Set>
                <Set name="Acceptors">2</Set>
                <Set name="statsOn">false</Set>
                <Set name="confidentialPort">8445</Set>
                <Set name="name">federationConn</Set>
                <Set name="lowResourcesConnections">20000</Set>
                <Set name="lowResourcesMaxIdleTime">5000</Set>
            </New>
        </Arg>
    </Call>

:host:
  This should be the loopback address as explained `Locking Down the
  Apache to Java EE Container Channel`_.

:port:
  In our examples we've been using port 8383 as the proxy port. The
  exact port is not important but it must be consistent with the
  Apache proxy port, the ``Connector`` declaration, and the port value
  in ``secureProxyPorts``.


Note, values in Jetty XML can also be parameterized so that they may
be passed from property files or set on the command line. Thus
typically the port is set within Jetty XML, but uses the Property
element to be customizable. Thus the above ``host`` and ``port``
properties could be specificed this way:

::

    <Set name="host">
        <Property name="jetty.host" default="127.0.0.1"/>
    </Set>
    <Set name="port">
        <Property name="jetty.port" default="8383"/>
    </Set>


The AAA system needs to know which port(s) the trusted Apache proxy
will be sending requests on so it can trust the request authentication
metadata. See `Declaring the Connector Ports for Authentication
Proxies`_ for more information). Set ``secureProxyPorts`` in the
FederationConfiguration.

************************************************
How Apache Identity Metadata is Processed in AAA
************************************************

`Figure 2.`_ and `Figure 3.`_ illustrates the fact the first stage in
processing a request from a user begins with Apache where the user is
authenticated and SSSD supplies additional metadata about the
user. The original request along with the metadata are subsequently
forwarded by Apache to the Java EE Container. `Figure 4.`_ illustrates
the processing inside the Java EE Container once it receives the
request on one of its secure connectors.


.. figure:: sssd_04.png
   :align: center

   _`Figure 4.`

:Step 1:
  One or more Connectors have been configured to listen for requests
  being forwarded from a trusted Apache instance. The Connector is
  configured to communicate using either the HTTP or AJP protocols.
  See `Exporting Environment Variables to the Proxy`_ for more
  information on selecting a proxy transport protocol.

:Step 2:
  The identity metadata bound to the request needs to be extracted
  differently depending upon whether HTTP or AJP is the transport
  protocol. To allow later stages in the pipeline to be ignorant of
  the transport protocol semantics the ``SssdFilter`` servlet filter
  is introduced. The ``SssdFilter`` wraps the ``HttpServletRequest``
  class and intercepts calls which might return the identity
  metadata. The wrapper in the filter looks in protocol specific
  locations for the metadata. In this manner users of the
  ``HttpServletRequest`` are isolated from protocol differences.


:Step 3:

  The ``ClaimAuthFilter`` is responsible for determining if identity
  metadata is bound to the request. If so all identity metadata is
  packaged into an assertion which is then handed off to
  ``SssdClaimAuth`` which will transform the identity metadata in the
  assertion into a AAA Claim which is the authorizing token for the user.

:Step 4:
  The ``SssdClaimAuth`` object is responsible for transforming the
  external federated identity metadata provided by Apache and SSSD into
  a AAA claim. The AAA claim is an authorization token which includes
  information about the user plus a set of roles. These roles provide the
  authorization to perform AAA tasks. Although how roles are assigned is
  flexible the expectation is domain and/or group membership will be the
  primary criteria for role assignment. Because deciding how to handle
  external federated identity metadata is site and deployment specific
  we need a loadable policy mechanism. This is accomplished by a set of
  transformation rules which transforms the incoming IdP identity
  metadata into a AAA claim. For greater clarity this important step is
  broken down into smaller units in the shaded box in `Figure 4.`_.

:Step 4.1:
  `The Mapping Rule Processor`_ is designed to accept a JSON object
  (set of key/value pairs) as input and emit a different JSON object
  as output effectively operating as a transformation engine on
  key/value pairs.

:Step 4.2:
  The input assertion is rewritten as a JSON object in the format
  required by the Mapping Rule Processor. The JSON assertion is then
  passed into the Mapping Rule Processor.

:Step 4.3:
  `The Mapping Rule Processor`_ identified as ``IdPMapper`` evaluates
  the input JSON assertion in the context of the mapping rules defined
  for the site deployment. If ``IdPMapper`` is able to successfully
  transform the input it will return a JSON object which we called the
  *mapped* result. If the input JSON assertion is not compatible with
  the site specific rules loaded into the ``IdPMapper`` then NULL is
  returned by the ``IdPMapper``.

:Step 4.4:
  If a mapped JSON object is returned by the ``IdPMapper`` the mapping
  was successful. The values in the mapped result are re-written into
  an AAA Claim token.

How Apache Identity Metadata is Mapped to AAA Values
====================================================

A federated IdP supplies metadata in a form unique to the IdP. This is
called an assertion. That assertion must be transformed into a format
and data understood by AAA. More importantly that assertion needs to
yield *authorization roles specific to AAA*. In `Figure 4.`_ Step 4.3
the ``IdPMapper`` provides the transformation from an external IdP
assertion to an AAA specific claim. It does this via a Mapping Rule
Processor which reads a site specific set of transformation
rules. These mapping rules define how to transform an external IdP
assertion into a AAA claim. The mapping rules also are responsible for
validating the external IdP claim to make sure it is consistent with
the site specific requirements. The operation of the Mapping Rule
Processor and the syntax of the mapping rules are defined in `The
Mapping Rule Processor`_.

Below is an example mapping rule which might be loaded into the
Mapping Rule Processor. It is assumed there are two AAA roles which
may be assigned [4]_:

``user``
  A role granting standard permissions for normal ODL users.

``admin``
  A special role granting full administrative permissions.

In this example assigning the ``user`` and ``admin`` roles
will be based on group membership in the following groups:

``odl_users``
  Members of this group are normal ODL users with restricted permissions.

``odl_admin``
  Members of this group are ODL administrators with permission to
  perform all operations.

Granting of the ``user`` and/or ``admin`` roles based on
membership in the ``odl_users`` and ``odl_admin`` is illustrated in
the follow mapping rule example which also extracts the user principal
and domain information in the preferred format for the site
(e.g. usernames are lowercase without domain suffixes and the domain
is uppercase and supplied separately).

_`Mapping Rule Example 1.`

::

  1   [
  2     {"mapping": {"ClientId": "$client_id",
  3            "UserId": "$user_id",
  4            "User": "$username",
  5            "Domain": "$domain",
  6            "roles": "$roles",
  7           },
  8      "statement_blocks": [
  9        [
  10         ["set", "$groups", []],
  11         ["set", "$roles", []]
  12       ],
  13       [
  14         ["in", "REMOTE_USER", "$assertion"],
  15         ["exit", "rule_fails", "if_not_success"],
  16         ["regexp", "$assertion[REMOTE_USER]", "(?<username>\\w+)@(?<domain>.+)"],
  17         ["exit", "rule_fails", "if_not_success"],
  18         ["lower", "$username", "$regexp_map[username]"],
  19         ["upper", "$domain", "$regexp_map[domain]"],
  20       ],
  21       [
  22         ["in", "REMOTE_USER_GROUPS", "$assertion"],
  23         ["exit", "rule_fails", "if_not_success"],
  24         ["split", "$groups", "$assertion[REMOTE_USER_GROUPS]", ":"],
  25       ],
  26       [
  27         ["in", "odl_users", "$groups"],
  28         ["continue", "if_not_success"],
  29         ["append", "$roles", "user"],
  30       ],
  31       [
  32         ["in", "odl_admin", "$groups"],
  33         ["continue", "if_not_success"],
  34         ["append", "$roles", "admin"]
  35       ],
  36       [
  37         ["unique", "$roles", "$roles"],
  38         ["length", "$n_roles", "$roles"],
  39         ["compare", "$n_roles", ">", 0],
  40         ["exit", "rule_fails", "if_not_success"],
  41       ],
  42     ]
  43    }
  44  ]

:Line 1:
  Starts a list of rules. In this example only 1 rule is defined. Each
  rule is a JSON object containing a ``mapping`` and a required list
  of ``statement_blocks``. The ``mapping`` may either be specified
  inside a rule as it is here or may be referenced by name in a table
  of mappings (this is easier to manage if you have a large number of
  rules and small number of mappings).

:Lines 2-7:
  Defines the JSON mapped result. Each key maps to AAA claim.  The
  value is a rule variable whose value will be substituted if the rule
  succeeds.  Thus for example the AAA claim value ``User`` will be
  assigned the value from the ``$username`` rule variable.
:Line 8:
  Begins the list of statement blocks. A statement must be contained
  inside a block.
:Lines 9-12:
  The first block usually initializes variables that will be
  referenced later. Here we initialize ``$groups`` and ``$roles`` to
  empty arrays. These arrays may be appended to in later blocks and
  may be referenced in the final ``mapping`` output.
:Lines 13-20:
  This block sets the user and domain information based on
  ``REMOTE_USER`` and exits the rule if ``REMOTE_USER`` is not defined.
:Lines 14-15:
  This test is critical, it assures ``REMOTE_USER`` is defined in the
  assertion, if not the rule is skipped because we depend on
  ``REMOTE_USER``.
:Lines 16-17:
  Performs a regular expression match against ``REMOTE_USER`` to split
  the username from the domain. The regular expression uses named
  groups, in this instance ``username`` and ``domain``. If the regular
  expression does not match the rule is skipped.
:Lines 18-19:
  These lines reference the previous result of the regular expression
  match which are stored in the special variable ``$regexp_map``. The
  username is converted to lower case and stored in ``$username`` and
  the domain is converted to upper case and stored in ``$domain``. The
  choice of case is purely by convention and site requirements.
:Lines 21-35:
  These 3 blocks assign roles based on group membership.
:Lines 21-25:
  Assures ``REMOTE_USER_GROUPS`` is defined in the assertion; if not, the
  rule is skipped. ``REMOTE_USER_GROUPS`` is colon separated list of group
  names. In order to operate on the individual group names appearing
  in ``REMOTE_USER_GROUPS`` line 24 splits the string on the colon
  separator and stores the result in the ``$groups`` array.
:Lines 27-30:
  This block assigns the ``user`` role if the user is a member of the
  ``odl_users`` group.
:Lines 31-35:
  This block assigns the ``admin`` role if the user is a
  member of the ``odl_admin`` group.
:Lines 36-41:
  This block performs final clean up actions for the rule. First it
  assures there are no duplicates in the ``$roles`` array by calling
  the ``unique`` function. Then it gets a count of how many items are
  in the ``$roles`` array and tests to see if it's empty. If there are
  no roles assigned the rule is skipped.
:Line 43:
  This is the end of the rule. If we reach the end of the rule it
  succeeds. When a rule succeeds the mapping associated with the rule
  is looked up. Any rule variable appearing in the mapping is
  substituted with its value.

Using the rules in `Mapping Rule Example 1.`_ and following example assertion
in JSON format:

_`Assertion Example 1.`

::

  {
    "REMOTE_USER": "TestUser@example.com",
    "REMOTE_AUTH_TYPE": "Negotiate",
    "REMOTE_USER_GROUPS": "odl_users:odl_admin",
    "REMOTE_USER_EMAIL": "test.user@example.com",
    "REMOTE_USER_FIRSTNAME": "Test",
    "REMOTE_USER_LASTNAME": "User"
  }

Then the mapper will return the following mapped JSON document. This
is the ``mapping`` defined on line 2 of `Mapping Rule Example 1.`_ with the
variables substituted after the rule successfully executed. Note any
valid JSON data type can be returned, in this example the ``null``
value is returned for ``ClientId`` and ``UserId``, normal strings for
``User`` and ``Domain`` and an array of strings for the ``roles`` value.

_`Mapped Result Example 1.`

::

  {
    "ClientId": null,
    "UserId": null,
    "User": "testuser",
    "Domain": "EXAMPLE.COM",
    "roles": ["user", "admin"]
  }


**************************
The Mapping Rule Processor
**************************

The Mapping Rule Processor is designed to be as flexible and generic
as possible. It accepts a JSON object as input and returns a JSON
object as output. JSON was chosen because virtually all data can be
represented in JSON, JSON has extensive support and JSON is human
readable. The rules loaded into the Mapping Rule Processor are also
expressed in JSON. One advantage of this is it makes it easy for a
site administrator to define hardcoded values which are always
returned and/or static tables of white and black listed users or users
who are always mapped into certain roles.

.. include:: mapping.rst

***********************
Security Considerations
***********************

Attack Vectors
==============

A Java EE Container fronted by Apache has by definition 2 major
components:

* Apache
* Java EE Container

Each of these needs to be secure in its own right. There is extensive
documentation on securing each of these components and the reader is
encouraged to review this material. For the purpose of this discussion
we are most interested in how Apache and the Java EE
Container cooperate to form an integrated security system. Because
Apache is performing authentication on behalf of the Java EE Container,
it views Apache as a trusted partner. Our primary concern is the
communication channel between Apache and the Java EE Container. We
must assure the Java EE Container knows who it's trusted partner is
and that it only accepts security sensitive data from that partner,
this can best be described as `The Proxy Problem`_.

Forged REMOTE_USER
------------------

HTTP request handling is often implemented as a processing pipeline
where individual handlers are passed the request, they may then attach
additional metadata to the request or transform it in some manner
before handing it off to the next stage in the pipeline. A request
handler may also short circuit the request processing pipeline and
cause a response to be generated. Authentication is typically
implemented an as early stage request handler. If a request gets past
an authentication handler later stage handlers can safely assume the
request belongs to an authenticated user. Authorization metadata may
also have been attached to the request. Later stage handlers use the
authentication/authorization metadata to make decisions as to whether
the operations in the request can be satisfied.

When a request is fielded by a traditional web server with CGI (Common
Gateway Interface, RFC 3875) the request metadata is passed via CGI
meta-variables. CGI meta-variables are often implemented as environment
variables, but in practical terms CGI metadata is really just a set of
name/value pairs a later stage (i.e. CGI script, servlet, etc.) can
reference to learn information about the request.

The CGI meta-variables REMOTE_USER and AUTH_TYPE relate to
authentication. REMOTE_USER is the identity of the authenticated user
and AUTH_TYPE is the authentication mechanism that was used to
authenticate the user.

**If a later stage request handler sees REMOTE_USER and AUTH_TYPE as
non-null values it assumes the user is fully authenticated! Therefore
is it essential REMOTE_USER and AUTH_TYPE can only enter the request
pipeline via a trusted source.**

The Proxy Problem
=================

In a traditional monolithic web server the CGI meta-variables are
created and managed by the web server, which then passes them to CGI
scripts and executables in a very controlled environment where they
execute in the context of the web server. Forgery of CGI
meta-variables is generally not possible unless the web server has
been compromised in some fashion.

However in our configuration the Apache web server acts as an identity
processor, which then forwards (i.e. proxies) the request to the Java
EE container (i.e Tomcat, Jetty, etc.). One could think of the Java
EE container as just another CGI script which receives CGI
meta-variables provided by the Apache web server. Where this analogy
breaks down is how Apache invokes the CGI script. Instead of forking a
child process where the child's environment and input/output pipes are
carefully controlled by Apache the request along with its additional
metadata is forwarded over a transport (typically TCP/IP) to another
process, the proxy, which listens on socket.

The proxy (in this case the Java EE container) reads the request and
the attached metadata and acts upon it. If the request read by the
proxy contains the REMOTE_USER and AUTH_TYPE CGI meta-variables the
proxy will consider the request **fully authenticated!**. Therefore
when the Java EE container is configured as a proxy it is
**essential** it only reads requests from a **trusted** Apache web
server. If any other client aside from the trusted Apache web server
is permitted to connect to the Java EE container that client could
present forged REMOTE_USER and AUTH_TYPE meta-variables, which would be
automatically accepted as valid thus opening a huge security hole.


Possible Approaches to Lock Down a Proxy Channel
================================================

Tomcat Valves
-------------

You can use a `Tomcat Remote Address Valve`_ valve to filter by IP or
hostname to only allow a subset of machines to connect. This can be
configured at the Engine, Host, or Context level in the
conf/server.xml by adding something like the following:

::

  <!-- allow only LAN IPs to connect -->
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
   allow="192.168.1.*">
  </Valve>

The problem with valves is they are a Tomcat only concept, the
``RemoteAddrValve`` only checks addresses, not port numbers (although
it should be easy to add port checking) and they don't offer anything
better than what is described in `Locking Down the Apache to Java EE
Container Channel`_, which is not container specific. Servlet filters
are always available regardless of the container the servlet is
running in. A filter can check both the address and port number and
refuse to operate on the request if the address and port are not known to
be a trusted authentication proxy. Also note that if the Java EE
Container is configured to accept connections other than from the
trusted HTTP proxy server (a very likely scenario) then filtering at
the connector level is not sufficient because a servlet which trusts
``REMOTE_USER`` must be assured the request arrived only on a
trusted HTTP proxy server connection, not one of the other possible
connections.

SSL/TLS with client auth
------------------------

SSL with client authentication is the ultimate way to lock down a HTTP
Server to Java EE Container proxy connection. SSL with client
authentication provides authenticity, integrity, and
confidentiality. However those desirable attributes come at a
performance cost which may be excessive. Unless a persistent TCP
connection is established between the HTTP server and the Java EE
Container a SSL handshake will need to occur on each request being
proxied, SSL handshakes are expensive. Given that the HTTP server and
the Java EE Container will likely be deployed on the same compute node
(or at a minimum on a secure subnet) the advantage of SSL for proxy
connections may not be warranted because other options are available
for these configuration scenarios; see `Locking Down the Apache to Java EE
Container Channel`_. Also note that if the Java EE
Container is configured to accept connections other than from the
trusted HTTP proxy server (a very likely scenario), then filtering at
the connector level is not sufficient because a servlet which trusts
``REMOTE_USER`` must be assured that the request arrived only on a
trusted HTTP proxy server connection, not one of the other possible
connections.


Java Security Manager Permissions
---------------------------------

The Java Security Manager allows you define permissions which are
checked at run time before code executes.
``java.net.SocketPermission`` and ``java.net.NetPermission`` would
appear to offer solutions for restricting which host and port a
request containing ``REMOTE_USER`` will be trusted. However security
permissions are applied *after* a request is accepted by a
connector. They are also more geared towards what connections code can
subsequently utilize as opposed to what connection a request was
presented on. Therefore security manager permissions seem to offer little
value for our purpose. One can simply test to see which host sent the
proxy request and on what port it arrived on by looking at the
connection information in the request. Restricting which proxies can
submit trusted requests is better handled at the level of the
connector, which unfortunately is a container implementation
issue. Tomcat and Jetty have different ways of handling connector
specifications.

AJP requiredSecret
------------------

The AJP protocol includes an attribute called ``requiredSecret``, which
can be used to secure the connection between AJP endpoints. When an
HTTP server sends an AJP proxy request to a Java EE Container it
embeds in the protocol transmission a string (``requiredSecret``)
known only to the HTTP server and the Java EE Container. The AJP
connector on the Java EE Container is configured with the
``requiredSecret`` value and will reject as unauthorized any AJP
requests whose ``requiredSecret`` does not match.

There are two problems with `requiredSecret``. First of all it's not
particularly secure. In fact, it's fundamentally no different than
sending a cleartext password. If the AJP request is not encrypted it
means the ``requiredSecret`` will be sent in the clear which is
probably one of the most egregious security mistakes. If the AJP
request is transmitted in a manner where the traffic can be sniffed, it
would be trivial to recover the ``requiredSecret`` and forge a request
with it. On the other hand encrypting the communication channel
between the HTTP server and the Java EE Container means using SSL
which is fairly heavyweight. But more to the point, if one is using
SSL to encrypt the channel there is a *far better* mechanism to ensure
the HTTP server is who it claims to be than embedding
``requiredSecret``. If one is using SSL you might as well use SSL
client authentication where the HTTP identifies itself via a client
certificate. SSL client authentication is a very robust authentication
mechanism. But doing SSL client authentication, or for that matter
just SSL encryption, for *every* AJP protocol request is prohibitively
expensive from a performance standpoint.

The second problem with ``requiredSecret`` is that despite being documented
in a number of places it's not actually implemented in Apache
``mod_proxy_ajp``. This is detailed in `bug 53098`_. You can set
``requiredSecret`` in the ``mod_proxy_ajp`` configuration, but it won't
be included in the wire protocol. There is a patch to implement
``requiredSecret`` but, it hasn't made it into any shipping version of
Apache yet. But even if ``requiredSecret`` was implemented it's not
useful. Also one could construct the equivalent of ``requiredSecret``
from other AJP attributes and/or an HTTP extension header but those
would suffer from the same security issues ``requiredSecret`` has,
therefore it's mostly pointless.

Java EE Container Issues
========================

Jetty Issues
------------

Jetty is a Java EE Container which can be used
as alternative to Tomcat. Jetty is an Eclipse project. Recent versions
of Jetty have dropped support for AJP; this is described in the
`Jetty AJP Configuration Guide`_ which states:

  Configuring AJP13 Using mod_jk or mod_proxy_ajp. Support for this
  feature has been dropped with Jetty 9.  If you feel this should be
  brought back please file a bug.

Eclipse `Bug 387928`_ *Retire jetty-ajp* was opened to track the
removal of AJP in Jetty and is now closed.

Tomcat Issues
-------------

You should refer the `Tomcat Security How-To`_ for a full discussion
of Tomcat security issues.

The tomcatAuthentication attribute is used with the AJP connectors to
determine if Tomcat should authenticate the user or if authentication
can be delegated to the reverse proxy that will then pass the
authenticated username to Tomcat as part of the AJP protocol.

The requiredSecret attribute in AJP connectors configures a shared
secret between Tomcat and the reverse proxy in front of Tomcat. It is used
to prevent unauthorized connections over AJP protocol.

Locking Down the Apache to Java EE Container Channel
====================================================

The recommended approach to lock down the proxy channel is:

  * Run both Apache and the servlet container on the same host.

  * Configure Apache to forward the proxy request on the loopback
    interface (e.g. 127.0.0.1 also known as ``localhost``). This
    prohibits any external IP address from connecting, only processes
    running on the locked down host can communicate over
    ``localhost``.

  * Reserve one or more ports for communication **exclusively** for
    proxy communication between Apache and the servlet container. The
    servlet container may listen on other ports for non-critical
    non-authenticated requests.

  * The ``ClaimAuthFilter`` that reads the identity metadata **must**
    assure that requests have arrived only on a **trusted port**. To
    achieve this the ``FederationConfiguration`` defines the
    ``secureProxyPorts`` configuration option. ``secureProxyPorts`` is
    a space delimited list of ports which during deployment the
    administrator has configured such that they are **exclusively**
    dedicated for use by the Apache server(s) providing authentication
    and identity information. These ports are set in the servlet
    container's ``Connector`` declarations. See `Declaring the
    Connector Ports for Authentication Proxies`_ for more
    information).

  * When the ``ClaimAuthFilter`` receives a request, the first thing
    it does is check the ``ServletRequest.getLocalPort()`` value and
    verifies it is a member of the ``secureProxyPorts`` configuration
    option. If the port is a member of ``secureProxyPorts``, it will
    trust every identity assertion found in the request. If the local
    port is not a member of ``secureProxyPorts``, a HTTP 401
    (unauthorized) error status will be returned for the request. A
    warning message will be logged the first time this occurs.


Declaring the Connector Ports for Authentication Proxies
--------------------------------------------------------

As described in `The Proxy Problem`_ the AAA authentication system
**must** confirm the request it is processing originated from a *trusted
HTTP proxy server*. This is accomplished with port isolation.

The administrator deploying a federated AAA solution with SSSD
identity lookups must declare in the AAA federation configuration
which ports the proxy requests from the trusted HTTP server will
arrive on by setting the ``secureProxyPorts`` configuration
item. These ports **must** only be used for the trusted HTTP proxy
server. The AAA federation software will not perform authentication
for any request arriving on a port other than those listed in
``secureProxyPorts``.

.. figure:: sssd_05.png
   :align: center

   _`Figure 5.`

``secureProxyPorts`` configuration option is set either in the
``federation.cfg`` file or in the
``org.opendaylight.aaa.federation.secureProxyPorts`` bundle
configuration. ``secureProxyPorts`` is a space-delimited list of port
numbers on which a trusted HTTP proxy performing authentication
forwards pre-authenticated requests. For example:

::

  secureProxyPorts=8383

Means a request which arrived on port 8383 is from a trusted HTTP
proxy server and the value of ``REMOTE_USER`` and other authentication
metadata in request can be trusted.

########
Appendix
########

*****************
CGI Export Issues
*****************

Apache processes requests as a series of steps in a pipeline
fashion. The ordering of these steps is important. Core Apache is
fairly minimal, most of Apache's features are supplied by loadable
modules. When a module is loaded it registers a set of *hooks*
(function pointers) which are to be run at specific stages in the
Apache request processing pipeline. Thus a module can execute code at
any of a number of stages in the request pipeline.

The user metadata supplied by Apache is initialized in two distinct
parts of Apache.

  1. an authentication module (e.g. mod_auth_kerb)
  2. the ``mod_lookup_identity`` module.

After successful authentication the authentication module will set the
name of the user principal and the mechanism used for authentication
in the request structure.

  * ``request->user``
  * ``request->ap_auth_type``

Authentication hooks run early in the request pipeline for the obvious
reason a request should not be processed if not authenticated. The
specific authentication module that runs is defined by ``Location``
directive in the Apache configuration which binds specific
authentication to specific URL's. The ``mod_lookup_identity`` module
must run *after* authentication module runs because it depends on
knowing who the authenticated principal is so it can lookup the data
on that principal.

When reading ``mod_lookup_identity`` documentation one often sees
references to the ``REMOTE_USER`` CGI environment variable with the
implication ``REMOTE_USER`` is how one accesses the name of the
authenticated principal. This is a bit misleading, ``REMOTE_USER`` is
a CGI environment variable. CGI environment variables are only set by
Apache when it believes the request is going to be processed by a CGI
implementation. In this case ``REMOTE_USER`` is initialized from the
``request->user`` value.

How is the authenticated principal actually forwarded to our proxy?
===================================================================

If we are using the AJP proxy protocol the ``mod_proxy_ajp`` module
when preparing the proxy request will read the value of
``request->user`` and insert it into the ``SC_A_REMOTE_USER`` AJP
attribute. On the receiving end ``SC_A_REMOTE_USER`` will be extracted
from the AJP request and used to populate the value returned
by``HttpServletRequest.getRemoteUser()``. The exchange of the
authenticated principal when using AJP is transparent to both the
sender and receiver, nothing special needs to be done. See
`Transporting Identity Metadata from Apache to a Java EE Servlet`_
for details on how metadata can be exchanged with the proxy.

However, if AJP is not being used to proxy the request the
authenticated principal must be passed through some other mechanism,
an HTTP extension header is the obvious solution. The Apache
``mod_headers`` module can be used to add HTTP request headers to the
proxy request, for example:

::

  RequestHeader set MY_HEADER MY_VALUE

Where does the value MY_VALUE come from? It can be hardcoded into the
``RequestHeader`` statement or it can reference an existing
environment variable like this:

::

  RequestHeader set MY_HEADER %{FOOBAR}e

where the notation ``%{FOOBAR}e`` is the contents of the environment
variable FOOBAR. Thus we might expect we could do this:

::

  RequestHeader set REMOTE_USER %{REMOTE_USER}e

The conundrum is the presumption the ``REMOTE_USER`` environment
variable has already been set at the time ``mod_headers`` executes the
``RequestHeader`` statement. Unfortunately this often is not the
case.

The Apache environment variables ``REMOTE_USER`` and ``AUTH_TYPE`` are
set by the Apache function ``ap_add_common_vars()`` defined in
server/util_script.c.  ``ap_add_common_vars()`` and is called by the
following modules:

  * mod_authnz_fcgi
  * mod_proxy_fcgi
  * mod_proxy_scgi
  * mod_isapi
  * mod_ext_filter
  * mod_include
  * mod_cgi
  * mod_cgid

Apache variables
================

Apache modules provide access to variables which can be referenced by
configuration directives. Unfortunately there isn't a lot of
uniformity to what the variables are and how they're referenced; it
mostly depends on how a given Apache module was implemented. As you
might imagine a bit of inconsistent historical cruft has accumulated
over the years, it can be confusing. The Apache Foundation is trying
to clean some of this up bringing uniformity to modules by utilizing
the common ``expr`` (expression) module `ap_expr`_. The idea being modules will
forgo their home grown expression syntax with its numerous quirks and
instead expose the common ``expr`` language. However this is a work in
progress and at the time of this writing only a few modules have acquired
``expr`` expression support.

Among the existing Apache modules there currently are three different
sets of variables.

  1. Server variables.
  2. Environment variables.
  3. SSL variables.

Server variables (item 1) are names given to internal values. The set
of names for server variables and what they map to are defined by the
module implementing the server variable lookup. For example
``mod_rewrite`` has its own variable lookup implementation.

Environment variables (item 2) are variables *exported* to a
subprocess. Internally they are stored in
``request->subprocess_env``. The most common use of environment
variables exported to a subprocess are the CGI variables.

SSL variables are connection specific values describing the SSL
connection. The lookup is implemented by ``ssl_var_lookup()``, which
given a variable name looks in a variety of internal data structures to
find the matching value.

The important thing to remember is **server variables != environment
variables**. This can be confusing because they often share the same
name. For example, there is the server variable ``REMOTE_USER`` and
there is the environment variable ``REMOTE_USER``. The environment
variable ``REMOTE_USER`` only exists if some module has called
``ap_add_common_vars()``. To complicate matters, some modules allow you
to access *server variables*, other modules allow you to access
*environment variables* and some modules provide access to both
*server variables* and *environment variables*.

Coming back to our goal of setting an HTTP extension header to the
value of ``REMOTE_USER``, we observe that ``mod_headers`` provides the
needed ``RequestHeader`` operation to set a HTTP header in the
request. Looking at the documentation for ``RequestHeader`` we see a
value can be specified with one of the following lookups:

%{VARNAME}e
  The contents of the environment variable VARNAME.

%{VARNAME}s
  The contents of the SSL environment variable VARNAME, if mod_ssl is enabled.

But wait! This only gives us access to *environment variables* and the
``REMOTE_USER`` environment variable is only set if
``ap_add_common_vars()`` is called by a module **after** an
authentication module runs! ``ap_add_common_vars()`` is usually only
invoked if the request is going to be passed to a CGI script. But
we're not doing CGI; instead we're proxying the request. The
likelihood the ``REMOTE_USER`` environment variable will be set is
quite low. See `Setting the REMOTE_USER environment variable`_.

``mod_headers`` is the only way to set a HTTP extension header and
``mod_headers`` only gives you access to environment variables and the
``REMOTE_USER`` environment variable is not set. Therefore if we're
not using AJP and must depend on setting a HTTP extension header for
``REMOTE_USER``, we have a **serious problem**.

But there is a solution; you can either try the machinations described
in `Setting the REMOTE_USER environment variable`_ or assure you're
running at least Apache version 2.4.10. In Apache 2.4.10 the
``mod_headers`` module added support for `ap_expr`_. `ap_expr`_
provides access to *server variables* by using the ``%{VARIABLE}``
notation. `ap_expr`_ also can lookup subprocess environment variables
and operating system environment variables using its ``reqenv()`` and
``osenv()`` functions respectively.

Thus the simple solution for exporting the ``REMOTE_USER`` HTTP
extension header if you're running Apache 2.4.10 or later is:

::

  RequestHeader set X-SSSD-REMOTE_USER expr=%{REMOTE_USER}

The ``expr=%{REMOTE_USER}`` in the above statement says pass
``%{REMOTE_USER}`` as an expression to `ap_expr`_, evaluate the
expression and return the value. In this case the expression
``%{REMOTE_USER}`` is very simple, just the value of the server
variables ``REMOTE_USER``. Because ``RequestHeader`` runs after
authentication ``request->user`` will have been set.

Setting the REMOTE_USER environment variable
============================================

If you do a web search on how to export ``REMOTE_USER`` in a HTTP
extension header for a proxy you will discover this is a common
problem that has frustrated a lot of people [2]_. The usual advice seems to
be to use ``mod_rewrite`` with a look-ahead. In fact this is even
documented in the `mod_rewrite documentation for REMOTE_USER`_ which says:

  %{LA-U:variable} can be used for look-aheads which perform an
  internal (URL-based) sub-request to determine the final value of
  variable. This can be used to access variable for rewriting which is
  not available at the current stage, but will be set in a later
  phase.

  For instance, to rewrite according to the REMOTE_USER variable from
  within the per-server context (httpd.conf file) you must use
  %{LA-U:REMOTE_USER} - this variable is set by the authorization
  phases, which come after the URL translation phase (during which
  mod_rewrite operates).

One suggested solution is this:

::

  RewriteCond %{LA-U:REMOTE_USER} (.+)
  RewriteRule .* - [E=RU:%1]
  RequestHeader set X_REMOTE_USER %{RU}e

1. The RewriteCond with the %{LA-U:} construct performs an internal
   redirect to obtain the value of ``REMOTE_USER`` *server variable*,
   if that value is non-empty because the (.+) regular expression
   matched the rewrite condition succeeds and the following
   RewriteRule executes.

2. The RewriteRule executes, the first parameter is a pattern, the
   second parameter is the replacement which can be followed by
   optional flags inside brackets. The .* pattern is a regular
   expression that matches anything, the - replacement is a special
   value which indicates no replacement is to be performed. In other
   words the pattern and replacement are no-ops and the RewriteRule is
   just being used for it's side effect defined in the flags. The
   E=NAME:VALUE notation says set the NAME environment variable to
   VALUE. In this case the environment variable is RU and the value is
   %1. The documentation for RewriteRule tells us that %N are
   back-references to the last matched RewriteCond pattern, in this
   case it's the value of ``REMOTE_USER``.

3. Finally ``RequestHeader`` sets the request header
   ``X_REMOTE_USER`` to the value of the ``RU`` environment variable.

Another suggested solution is this:

::

  RewriteRule .* - [E=REMOTE_USER:%{LA-U:REMOTE_USER}]

The Problem with mod_rewrite lookahead
--------------------------------------

I **do not recommend** using mod_rewrite's lookahead to gain access to
authentication data values. Although the above suggestions will work
to get access to ``REMOTE_USER`` it is *extremely inefficient* because
it causes Apache to reprocess the request with an internal
redirect. The documentation suggests a lookahead reference will cause
one internal redirect. However from examining Apache debug logs the
``mod_rewite`` lookahead caused ``mod_lookup_identity`` to be invoked
**11 times** while handling one request. If the ``mod_rewrite``
lookahead is removed and another technique is used to get access to
``REMOTE_USER`` then ``mod_lookup_identity`` is invoked exactly once
as expected.

But it's not just ``REMOTE_USER`` which we need access to, we also need
to reference ``AUTH_TYPE`` which has the identical issues associated
with ``REMOTE_USER``. If an equivalent ``mod_rewrite`` block is added
to the configuration for ``AUTH_TYPE`` so that both ``REMOTE_USER``
and ``auth_type`` are resolved using a lookahead Apache appears to go
into an infinite loop and the request stalls.

I tried to debug what was occurring when Apache was configured this way
and why it seemed to be executing the same code over and over but I
was not able to figure it out. My conclusion is **using mod_rewrite
lookahead's is not a viable solution!** Other web posts also make
reference to the inefficiency but they seem to be unaware of just how
bad it is.

.. [1]
   Tomcat has a bug/feature, not all attributes are enumerated by
   getAttributeNames() therefore getAttributeNames() cannot be used to
   obtain the full set of attributes. However if you know the name of
   the attribute a priori you can call getAttribute() and obtain the
   value. Therefore we maintain a list of attribute names
   (httpAttributes) which will be used to call getAttribute() with so we
   don't miss essential attributes.

   This is the Tomcat bug, note it is marked WONTFIX. Bug 25363 -
   request.getAttributeNames() not working properly Status: RESOLVED
   WONTFIX https://issues.apache.org/bugzilla/show_bug.cgi?id=25363

   The solution adopted by Tomcat is to document the behavior in the
   "The Apache Tomcat Connector - Reference Guide" under the JkEnvVar
   property where is says:

   You can retrieve the variables on Tomcat as request attributes via
   request.getAttribute(attributeName). Note that the variables send via
   JkEnvVar will not be listed in request.getAttributeNames().

.. [2]
   Some examples of posts concerning the export of ``REMOTE_USER``  include:
   http://www.jaddog.org/2010/03/22/how-to-proxy-pass-remote_user/ and
   http://serverfault.com/questions/23273/apache-proxy-passing-on-remote-user-to-backend-server/

.. [3]
   The ``mod_lookup_identity`` ``LookupUserGroups`` option accepts an
   optional parameter to specify the separator used to separate group
   names. By convention this is normally the colon (:) character. In
   our examples we explicitly specify the colon separator because the
   mapping rules split the value found in ``REMOTE_USER_GROUPS`` on
   the colon character.

.. [4]
   The example of using the `The Mapping Rule Processor`_ to establish
   the set of roles assigned to a user based on group membership is
   for illustrative purposes in order to show features of the
   federated IdP and mapping mechanism. Role assignment in AAA may be
   done in other ways. For example an unscoped token without roles can
   be used to acquire a scoped token with roles by presenting it to
   the appropriate REST API endpoint. In actual deployments this may
   be preferable because it places the responsibility of deciding who
   has what role/permission on what part of the controller/network
   resources more in the hands of the SDN controller administrator
   than the IdP administrator.

.. _FreeIPA: http://www.freeipa.org/

.. _SSSD: https://fedorahosted.org/sssd/

.. _mod_identity_lookup: http://www.adelton.com/apache/mod_lookup_identity/

.. _AJP: http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html

.. _Tomcat Security How-To: http://tomcat.apache.org/tomcat-7.0-doc/security-howto.html

.. _The Apache Tomcat Connector - Generic HowTo: http://tomcat.apache.org/connectors-doc/generic_howto/printer/proxy.html

.. _CGI RFC: http://www.ietf.org/rfc/rfc3875

.. _ap_expr: http://httpd.apache.org/docs/current/expr.html

.. _mod_rewrite documentation for REMOTE_USER: http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond

.. _bug 53098: https://issues.apache.org/bugzilla/show_bug.cgi?id=53098

.. _Jetty AJP Configuration Guide: http://wiki.eclipse.org/Jetty/Howto/Configure_AJP13

.. _Bug 387928: https://bugs.eclipse.org/bugs/show_bug.cgi?id=387928

.. _Tomcat Remote Address Valve: http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html#Remote_Address_Filter