Skip to content

Commit 6addbd8

Browse files
[XrdHttp] Add Kerberos SPNEGO authentication over HTTPS
Enable GSS-API Negotiate authentication for XrdHTTP so clients such as curl can authenticate with --negotiate. Configure via http.auth krb5 with a keytab and HTTP service principal, and add integration tests. Assisted-by: Cursor:Composer-2.5 CursorAI Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ace1d2a commit 6addbd8

12 files changed

Lines changed: 778 additions & 2 deletions

cmake/FindKerberos5.cmake

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,19 @@ else()
2929
${LIBRARY_PATH_PREFIX}
3030
${LIB_SEARCH_OPTIONS})
3131

32+
find_library(
33+
GSSAPI_KRB5_LIBRARY
34+
NAMES gssapi_krb5 gss
35+
HINTS
36+
${KERBEROS5_ROOT_DIR}
37+
PATH_SUFFIXES
38+
${LIBRARY_PATH_PREFIX}
39+
${LIB_SEARCH_OPTIONS})
40+
3241
set( KERBEROS5_LIBRARIES ${KERBEROS5_LIBRARY} ${COM_ERR_LIBRARY} )
42+
if( GSSAPI_KRB5_LIBRARY )
43+
list( APPEND KERBEROS5_LIBRARIES ${GSSAPI_KRB5_LIBRARY} )
44+
endif()
3345

3446
find_package_handle_standard_args(
3547
Kerberos5

src/XrdHttp/CMakeLists.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ endif()
55
# Note that XrdHttpUtils is marked as a shared library as XrdHttp plugins
66
# are expected to link against it for the XrdHttpExt class implementations.
77

8-
add_library(XrdHttpUtils SHARED
8+
set(XRDHTTPUTILS_SRCS
99
XrdHttpChecksum.cc XrdHttpChecksum.hh
1010
XrdHttpChecksumHandler.cc XrdHttpChecksumHandler.hh
1111
XrdHttpExtHandler.cc XrdHttpExtHandler.hh
@@ -22,6 +22,12 @@ add_library(XrdHttpUtils SHARED
2222
XrdHttpHeaderUtils.cc XrdHttpHeaderUtils.hh
2323
)
2424

25+
if(BUILD_KRB5)
26+
list(APPEND XRDHTTPUTILS_SRCS XrdHttpKrb5.cc XrdHttpKrb5.hh)
27+
endif()
28+
29+
add_library(XrdHttpUtils SHARED ${XRDHTTPUTILS_SRCS})
30+
2531
set_target_properties(XrdHttpUtils
2632
PROPERTIES
2733
SOVERSION ${XRootD_VERSION_MAJOR}
@@ -38,6 +44,12 @@ target_link_libraries(XrdHttpUtils
3844
OpenSSL::Crypto
3945
)
4046

47+
if(BUILD_KRB5)
48+
target_compile_definitions(XrdHttpUtils PRIVATE HAVE_HTTP_KRB5)
49+
target_include_directories(XrdHttpUtils PRIVATE ${KERBEROS5_INCLUDE_DIR})
50+
target_link_libraries(XrdHttpUtils PRIVATE ${KERBEROS5_LIBRARIES})
51+
endif()
52+
4153
set(XrdHttp XrdHttp-${PLUGIN_VERSION})
4254
add_library(${XrdHttp} MODULE XrdHttpModule.cc)
4355
target_link_libraries(${XrdHttp} PRIVATE XrdUtils XrdHttpUtils)

src/XrdHttp/README-KRB5.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Kerberos authentication over HTTPS
2+
3+
XrdHTTP supports Kerberos 5 client authentication over HTTPS using
4+
[SPNEGO](https://datatracker.ietf.org/doc/html/rfc4559) (`WWW-Authenticate:
5+
Negotiate`). This is the same mechanism used by tools such as
6+
[curl](https://curl.se/docs/manpage.html) with `--negotiate`.
7+
8+
The native XRootD protocol continues to use the `XrdSeckrb5` security plugin
9+
(`sec.protocol krb5`). HTTP Kerberos is configured separately via
10+
`http.auth krb5` and authenticates requests before the HTTP bridge login.
11+
12+
## Requirements
13+
14+
Build XRootD with Kerberos and HTTP support enabled:
15+
16+
```bash
17+
cmake .. -DENABLE_KRB5=ON -DENABLE_HTTP=ON
18+
```
19+
20+
The build links against the Kerberos and GSS-API libraries (`krb5`,
21+
`gssapi_krb5`).
22+
23+
On the server:
24+
25+
- HTTPS must be configured (`xrd.tls`, and typically `xrd.tlsca`).
26+
- A keytab containing the HTTP service principal for the host.
27+
- TLS client certificate authentication should usually be disabled when
28+
using Kerberos (`http.tlsclientauth off`), unless both are intentionally
29+
required.
30+
31+
On the client:
32+
33+
- A valid Kerberos ticket (for example via `kinit`).
34+
- An HTTP client that supports SPNEGO, such as curl 7.40 or later.
35+
36+
## Service principal
37+
38+
HTTP clients request a ticket for the **`HTTP`** service class, not `host`.
39+
The principal in the keytab and in the server configuration must therefore
40+
look like:
41+
42+
```text
43+
HTTP/hostname@REALM
44+
```
45+
46+
For example:
47+
48+
```text
49+
HTTP/localhost@EXAMPLE.ORG
50+
HTTP/myserver.example.org@EXAMPLE.ORG
51+
```
52+
53+
Create the principal and add it to the keytab with your KDC tools. With MIT
54+
Kerberos:
55+
56+
```bash
57+
kadmin.local -q "addprinc -randkey HTTP/myserver.example.org@EXAMPLE.ORG"
58+
kadmin.local -q "ktadd -k /etc/krb5.keytab HTTP/myserver.example.org"
59+
```
60+
61+
The configuration directive accepts the `<host>` keyword, which is expanded
62+
to the local hostname at startup (same convention as `sec.protocol krb5`):
63+
64+
```text
65+
HTTP/<host>@EXAMPLE.ORG
66+
```
67+
68+
## Server configuration
69+
70+
Minimal example:
71+
72+
```cfg
73+
xrd.protocol https:443 libXrdHttp.so
74+
75+
xrd.tlsca certfile /etc/grid-security/certificates/ca.pem
76+
xrd.tls /etc/grid-security/hostcert.pem /etc/grid-security/hostkey.pem
77+
78+
http.tlsclientauth off
79+
http.auth krb5 /etc/krb5.keytab HTTP/<host>@EXAMPLE.ORG
80+
81+
all.export /
82+
```
83+
84+
Directive syntax:
85+
86+
```text
87+
http.auth krb5 <keytab> <principal>
88+
```
89+
90+
| Argument | Description |
91+
|--------------|--------------------------------------------------|
92+
| `keytab` | Path to the server keytab file |
93+
| `principal` | HTTP service principal (see above) |
94+
95+
When `http.auth krb5` is enabled:
96+
97+
- Only **HTTPS** connections are accepted for authenticated access. Plain
98+
HTTP receives `403 Forbidden`.
99+
- Unauthenticated requests receive `401 Unauthorized` with
100+
`WWW-Authenticate: Negotiate`.
101+
- After a successful handshake, the client identity is stored in
102+
`SecEntity.name` (username part of the principal) and
103+
`SecEntity.moninfo` (full principal). `SecEntity.prot` is set to `krb5`.
104+
105+
## Client usage
106+
107+
Obtain a Kerberos ticket, then use curl with `--negotiate`:
108+
109+
```bash
110+
export KRB5CCNAME=/tmp/krb5cc_${UID} # if needed
111+
kinit alice@EXAMPLE.ORG
112+
113+
curl --negotiate -u : \
114+
--cacert /path/to/ca.pem \
115+
https://myserver.example.org:443/path/to/file
116+
```
117+
118+
The `-u :` option is required by curl: it enables Negotiate authentication
119+
without sending HTTP Basic credentials.
120+
121+
Other examples:
122+
123+
```bash
124+
# Upload a file
125+
curl --negotiate -u : \
126+
--cacert /path/to/ca.pem \
127+
-T local.dat \
128+
https://myserver.example.org/data/local.dat
129+
130+
# HEAD request
131+
curl --negotiate -u : \
132+
--cacert /path/to/ca.pem \
133+
-I \
134+
https://myserver.example.org/data/local.dat
135+
```
136+
137+
The hostname in the URL must match the service principal (or its DNS alias
138+
as known to the KDC). curl requests a ticket for `HTTP/hostname@REALM` based
139+
on the URL host.
140+
141+
## Authentication flow
142+
143+
```text
144+
Client Server
145+
| |
146+
| GET /file (HTTPS, no Authorization) |
147+
|-------------------------------------->|
148+
| |
149+
| 401 WWW-Authenticate: Negotiate |
150+
|<--------------------------------------|
151+
| |
152+
| GET /file |
153+
| Authorization: Negotiate <token> |
154+
|-------------------------------------->|
155+
| |
156+
| 200 OK (or further 401 rounds) |
157+
|<--------------------------------------|
158+
```
159+
160+
The server uses GSS-API `gss_accept_sec_context` to validate the SPNEGO
161+
token. Multi-round handshakes reuse the same TCP connection; the GSS
162+
context is kept per connection until authentication completes.
163+
164+
## Testing
165+
166+
An integration test is provided under `tests/XRootD/`:
167+
168+
- `httpkrb5.cfg` — server configuration
169+
- `httpkrb5.sh` — curl-based test script
170+
171+
The test requires the Kerberos and TLS test fixtures and runs on Linux CI
172+
(`BUILD_KRB5 AND NOT APPLE`). It verifies upload, download, HEAD, DELETE,
173+
and rejection of unauthenticated requests.
174+
175+
## Troubleshooting
176+
177+
| Symptom | Likely cause |
178+
|---------|----------------|
179+
| `403 Kerberos authentication requires HTTPS` | Request arrived over plain HTTP while `http.auth krb5` is enabled |
180+
| `401` on every request | No valid client ticket; run `kinit` and check `klist` |
181+
| `401` / GSS failure after token sent | Wrong service principal in keytab; ensure `HTTP/host@REALM`, not `host/host@REALM` |
182+
| curl does not send Negotiate | Missing `-u :` or curl built without SPNEGO/GSS support |
183+
| Principal mismatch | URL hostname does not match the `HTTP/` principal in the keytab |
184+
185+
Enable HTTP tracing to inspect the handshake:
186+
187+
```cfg
188+
http.trace auth
189+
```
190+
191+
Server-side Kerberos debug can also be enabled with the usual `XrdSecDEBUG`
192+
environment variable used by `XrdSeckrb5`.

0 commit comments

Comments
 (0)