Skip to content

Commit 4e69e16

Browse files
dongjoon-hyunHyukjinKwon
authored andcommitted
[SPARK-49104][CORE][DOCS] Document JWSFilter usage in Spark UI and REST API and rename parameter to secretKey
### What changes were proposed in this pull request? This PR aims the following. - Document `JWSFilter` and its usage in `Spark UI` and `REST API` - `Spark UI` section of `Configuration` page - `Spark Security` page - `Spark Standalone` page - Rename the parameter `key` to `secretKey` to redact it in Spark Driver UI and Spark Master UI. ### Why are the changes needed? To apply recent new security features - #47575 - #47595 ### Does this PR introduce _any_ user-facing change? No because this is a new feature of Apache Spark 4.0.0. ### How was this patch tested? Pass the CIs and manual review. - `spark-standalone.html` ![Screenshot 2024-08-03 at 22 40 53](https://github.com/user-attachments/assets/f1b95a01-c14b-4f14-96b6-3181afaf6f9f) - `security.html` ![Screenshot 2024-08-03 at 22 39 00](https://github.com/user-attachments/assets/8413f6a3-47df-4d71-87ee-25ab32171c6c) ![Screenshot 2024-08-03 at 22 39 51](https://github.com/user-attachments/assets/01546724-d5b5-40d5-a980-236f9d13ae81) - `configuration.html` ![Screenshot 2024-08-03 at 22 38 07](https://github.com/user-attachments/assets/c0845a7f-6ae1-4194-b98a-68d7442c9785) ### Was this patch authored or co-authored using generative AI tooling? No. Closes #47596 from dongjoon-hyun/SPARK-49104. Authored-by: Dongjoon Hyun <[email protected]> Signed-off-by: Hyukjin Kwon <[email protected]>
1 parent 250a4cc commit 4e69e16

File tree

6 files changed

+40
-11
lines changed

6 files changed

+40
-11
lines changed

core/src/main/scala/org/apache/spark/ui/JWSFilter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import jakarta.servlet.http.{HttpServletRequest, HttpServletResponse}
3131
* Like the other UI filters, the following configurations are required to use this filter.
3232
* {{{
3333
* - spark.ui.filters=org.apache.spark.ui.JWSFilter
34-
* - spark.org.apache.spark.ui.JWSFilter.param.key=BASE64URL-ENCODED-YOUR-PROVIDED-KEY
34+
* - spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-YOUR-PROVIDED-KEY
3535
* }}}
3636
* The HTTP request should have {@code Authorization: Bearer <jws>} header.
3737
* {{{
@@ -52,7 +52,7 @@ private class JWSFilter extends Filter {
5252
* - WeakKeyException will happen if the user-provided value is insufficient
5353
*/
5454
override def init(config: FilterConfig): Unit = {
55-
key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(config.getInitParameter("key")));
55+
key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(config.getInitParameter("secretKey")));
5656
}
5757

5858
override def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {

core/src/test/scala/org/apache/spark/deploy/rest/StandaloneRestSubmitSuite.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ class StandaloneRestSubmitSuite extends SparkFunSuite {
509509
rpcEnv = Some(RpcEnv.create("rest-with-filter", localhost, 0, conf, securityManager))
510510
val fakeMasterRef = rpcEnv.get.setupEndpoint("fake-master", new DummyMaster(rpcEnv.get))
511511
conf.set(MASTER_REST_SERVER_FILTERS.key, "org.apache.spark.ui.JWSFilter")
512-
conf.set("spark.org.apache.spark.ui.JWSFilter.param.key", TEST_KEY)
512+
conf.set("spark.org.apache.spark.ui.JWSFilter.param.secretKey", TEST_KEY)
513513
server = Some(new StandaloneRestServer(localhost, 0, conf, fakeMasterRef, "spark://fake:7077"))
514514
server.get.start()
515515
}
@@ -521,7 +521,7 @@ class StandaloneRestSubmitSuite extends SparkFunSuite {
521521
rpcEnv = Some(RpcEnv.create("rest-with-filter", localhost, 0, conf, securityManager))
522522
val fakeMasterRef = rpcEnv.get.setupEndpoint("fake-master", new DummyMaster(rpcEnv.get))
523523
conf.set(MASTER_REST_SERVER_FILTERS.key, "org.apache.spark.ui.JWSFilter")
524-
conf.set("spark.org.apache.spark.ui.JWSFilter.param.key", TEST_KEY)
524+
conf.set("spark.org.apache.spark.ui.JWSFilter.param.secretKey", TEST_KEY)
525525
server = Some(new StandaloneRestServer(localhost, 0, conf, fakeMasterRef, "spark://fake:7077"))
526526
val port = server.get.start()
527527
val masterUrl = s"spark://$localhost:$port"

core/src/test/scala/org/apache/spark/ui/JWSFilterSuite.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class JWSFilterSuite extends SparkFunSuite {
4848
test("Succeed to initialize") {
4949
val filter = new JWSFilter()
5050
val params = new JHashMap[String, String]
51-
params.put("key", TEST_KEY)
51+
params.put("secretKey", TEST_KEY)
5252
filter.init(new DummyFilterConfig(params))
5353
}
5454

@@ -59,7 +59,7 @@ class JWSFilterSuite extends SparkFunSuite {
5959

6060
val filter = new JWSFilter()
6161
val params = new JHashMap[String, String]
62-
params.put("key", TEST_KEY)
62+
params.put("secretKey", TEST_KEY)
6363
val conf = new DummyFilterConfig(params)
6464
filter.init(conf)
6565

@@ -84,7 +84,7 @@ class JWSFilterSuite extends SparkFunSuite {
8484

8585
val filter = new JWSFilter()
8686
val params = new JHashMap[String, String]
87-
params.put("key", TEST_KEY)
87+
params.put("secretKey", TEST_KEY)
8888
val conf = new DummyFilterConfig(params)
8989
filter.init(conf)
9090

docs/configuration.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ Apart from these, the following properties are also available, and may be useful
794794
</tr>
795795
<tr>
796796
<td><code>spark.redaction.regex</code></td>
797-
<td>(?i)secret|password|token|access[.]key</td>
797+
<td>(?i)secret|password|token|access[.]?key</td>
798798
<td>
799799
Regex to decide which Spark configuration properties and environment variables in driver and
800800
executor environments contain sensitive information. When this regex matches a property key or
@@ -1733,6 +1733,10 @@ Apart from these, the following properties are also available, and may be useful
17331733
<br /><code>spark.ui.filters=com.test.filter1</code>
17341734
<br /><code>spark.com.test.filter1.param.name1=foo</code>
17351735
<br /><code>spark.com.test.filter1.param.name2=bar</code>
1736+
<br />
1737+
<br />Note that some filter requires additional dependencies. For example,
1738+
the built-in <code>org.apache.spark.ui.JWSFilter</code> requires
1739+
<code>jjwt-impl</code> and <code>jjwt-jackson</code> jar files.
17361740
</td>
17371741
<td>1.0.0</td>
17381742
</tr>

docs/security.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ specified below, the secret must be defined by setting the `spark.authenticate.s
4949
option. The same secret is shared by all Spark applications and daemons in that case, which limits
5050
the security of these deployments, especially on multi-tenant clusters.
5151

52-
The REST Submission Server does not support authentication. You should
53-
ensure that all network access to the REST API (port 6066 by default)
54-
is restricted to hosts that are trusted to submit jobs.
52+
The REST Submission Server supports HTTP `Authorization` header with
53+
a cryptographically signed JSON Web Token via `JWSFilter`.
54+
To enable authorization, Spark Master should have
55+
`spark.master.rest.filters=org.apache.spark.ui.JWSFilter` and
56+
`spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-KEY` configurations, and
57+
client should provide HTTP `Authorization` header which contains JSON Web Token signed by
58+
the shared secret key.
5559

5660
### YARN
5761

@@ -351,6 +355,8 @@ The following options control the authentication of Web UIs:
351355
<td><code>spark.ui.filters</code></td>
352356
<td>None</td>
353357
<td>
358+
Spark supports HTTP <code>Authorization</code> header with a cryptographically signed
359+
JSON Web Token via <code>org.apache.spark.ui.JWSFilter</code>. <br />
354360
See the <a href="configuration.html#spark-ui">Spark UI</a> configuration for how to configure
355361
filters.
356362
</td>

docs/spark-standalone.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ SPARK_MASTER_OPTS supports the following system properties:
263263
</td>
264264
<td>1.3.0</td>
265265
</tr>
266+
<tr>
267+
<td><code>spark.master.rest.filters</code></td>
268+
<td>(None)</td>
269+
<td>
270+
Comma separated list of filter class names to apply to the Master REST API.
271+
</td>
272+
<td>4.0.0</td>
273+
</tr>
266274
<tr>
267275
<td><code>spark.master.useAppNameAsAppId.enabled</code></td>
268276
<td><code>false</code></td>
@@ -676,6 +684,17 @@ The following is the response from the REST API for the above <code>create</code
676684
}
677685
```
678686

687+
When Spark master requires HTTP <code>Authorization</code> header via
688+
<code>spark.master.rest.filters=org.apache.spark.ui.JWSFilter</code> and
689+
<code>spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-KEY</code>
690+
configurations, <code>curl</code> CLI command can provide the required header like the following.
691+
692+
```bash
693+
$ curl -XPOST http://IP:PORT/v1/submissions/create \
694+
--header "Authorization: Bearer USER-PROVIDED-WEB-TOEN-SIGNED-BY-THE-SAME-SHARED-KEY"
695+
...
696+
```
697+
679698
For <code>sparkProperties</code> and <code>environmentVariables</code>, users can use place
680699
holders for server-side environment variables like the following.
681700

0 commit comments

Comments
 (0)