OSS - C SDK 深度案例

本文涉及的产品
对象存储 OSS,20GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
视频点播 VOD,流量+存储+转码
简介: 客户使用 OSS C SDK 3.5 版本,通过 get_object_to_local_file 方法直接下载 OSS文件到本地,测试过程返回 403 签名不对

作者:张医博

概述:

客户使用 OSS C SDK 3.5 版本,通过 get_object_to_local_file 方法直接下载 OSS文件到本地,测试过程返回 403 签名不对;

搜集信息

挖取有价值的信息很重要,通过基础信息可以筛选下客户上传日志的详细描述,通过堆栈报错可以找到客户大概原因;

  • OSS response header 中包含 requestID ,记录客户端请求到 OSS 的详细日志;
  • 堆栈报错信息;

分析 requestID 对应的服务端错误日志

服务端返回 4xx 2xx 3xx 500 的状态码都会返回 requestID;通过 requestID 我们可以过滤到服务的错误日志如下;

java
Method:HEAD    Host:oss-cn-shanghai.aliyuncs.com    URI:/vod%2Fplat%2Fupdate%2Fhaimu_21_9%2FBasketball.zip

StringToSign:HEAD\n\n\nWed, 06 Nov 2019 07:55:44 GMT\n/vod/plat/update/haimu_21_9/Basketball.zip
UserSignature:LIMmOQ/5TLs2Gk24MuNVRl+Lu0Q=    OssServerSignature:P+YZauMD+fRtZjeMWkauuN6A4eU=

ErrorCode:SignatureDoesNotMatch    ErrorMsg:The request signature we calculated does not match the signature you provided. Check your key and signing method.

image.png

  • 通过日志可以明显看出客户端签名和服务端签名不一致导致校验失败;
  • 根据签名算法可以知道这算计算签名的参数中 /vod/plat/update/haimu_21_9/Basketball.zip 其中 vod 是 bucket 的位置;
  • Host: oss-cn-shanghai.aliyuncs.com 这个位置是错误,因为正常的直接访问 OSS ,Host 正确写法应该是 bucket.oss-cn-region.aliyuncs.com(region 替换成 bucket 所在地区)

问题分析

怀疑1、

已经知道问题出现在签名不对,而且 Host 也不对;问题出现在这里,但是如果 Host ,但 OSS 还能识别出 bucket ,这很奇怪;于是和用户沟通, vod 并不是用户的 bucket,那这个桶是怎么获取到的呢?遂让用户在端上进行 Wireshark 抓包, 得到报文

image.png

  1. 从报文中可以看到 Host 是客户端传的时候就没有 bucket ;
  2. 而 vod 是用户 objectkey 文件前缀而已;
  3. 通过上述几点可以知道问题一定是用户代码上哪里出现的变量写错或者用法不对;

怀疑2、

请用户在本地 debug ,将关键签名的变量信息打印出来看是否完整;

关键信息位置

image.png

用户自己 debug 出来变量的位置

image.png

通过客户 debug 看到变量都正确,为什么还会出现 bucket “丢失” 的情况呢;

怀疑3、

分析源码

int get_object_to_local_file(char *bucketname, char *objectname, char *filename)
{
    aos_pool_t *p = NULL;
    aos_string_t bucket;
    aos_string_t object;
    oss_request_options_t *options = NULL;
    aos_table_t *headers = NULL;
    aos_table_t *params = NULL;
    aos_table_t *resp_headers = NULL;
    aos_status_t *s = NULL;
    aos_string_t file;
    int is_cname = 1;

    if (bucketname == NULL || objectname == NULL || filename == NULL)
        return -1;

    aos_pool_create(&p, NULL);
    options = oss_request_options_create(p);
    init_sample_request_options(options, is_cname);
    aos_str_set(&bucket, bucketname);
    aos_str_set(&object, objectname);
    headers = aos_table_make(p, 0);
    aos_str_set(&file, filename);

    s = oss_get_object_to_file(options, &bucket, &object, headers,
        params, &file, &resp_headers);
    if (aos_status_is_ok(s))
    {
        printf("get object to local file succeeded\n");
    }
    else
    {
        printf("get object to local file failed\n");
        aos_pool_destroy(p);
        return -2;
    }
    aos_pool_destroy(p);
    return 0;
}

——> 从源码中可以,getobject 下载时形参都是传进来的,既然客户 debug 的变量都是正确的,那么肯定不是传进来变量 ,问题一定是方法内的常量导致;对比官方源码:

void get_object_to_local_file()
{
    aos_pool_t *p = NULL;
    aos_string_t bucket;
    char *download_filename = "get_object_to_local_file.txt";
    aos_string_t object;
    int is_cname = 0;
    oss_request_options_t *options = NULL;
    aos_table_t *headers = NULL;
    aos_table_t *params = NULL;
    aos_table_t *resp_headers = NULL;
    aos_status_t *s = NULL;
    aos_string_t file;

    aos_pool_create(&p, NULL);
    options = oss_request_options_create(p);
    init_sample_request_options(options, is_cname);
    aos_str_set(&bucket, BUCKET_NAME);
    aos_str_set(&object, OBJECT_NAME);
    headers = aos_table_make(p, 0);
    aos_str_set(&file, download_filename);

    s = oss_get_object_to_file(options, &bucket, &object, headers, 
                               params, &file, &resp_headers);
    if (aos_status_is_ok(s)) {
        printf("get object to local file succeeded\n");
    } else {
        printf("get object to local file failed\n");
    }

    aos_pool_destroy(p);
}

——> 从源码和用户的源码对比发现有个 cname 的参数,这个参数用户端是 1,官网的源码是 0,参数的含义,是否启用 cname 方式上传, cname 简称别名,比如用户给 bucket 绑定一个备案的域名后,那个域名就是 cname;我们追下 cname 的用法。

if (options->config->is_cname ||
     is_valid_ip(raw_endpoint_str))
 {
     req->host = apr_psprintf(options->pool, "%.*s",
             raw_endpoint.len, raw_endpoint.data);
     req->uri = apr_psprintf(options->pool, "%.*s", bucket->len,
                             bucket->data);
 } else {
     req->host = apr_psprintf(options->pool, "%.*s.%.*s",
             bucket->len, bucket->data,
             raw_endpoint.len, raw_endpoint.data);
     req->uri = apr_psprintf(options->pool, "%s", "");
 }

——> 从这端代码中可以看到,启用了 cname 后,Host 直接取的 endpoint 值;基本上找到了和用户用了 cname 有关系;

  • 经过后台分析发现用户端并没有绑定 oss+域名 的映射关系,而 SDK 在启动了 cname 上传,把 endpoint 当做完成的域名,没有把用户的 bucket name 拼到 Host 中,认为启用 cname 后, endpoint 就是完成域名;(设计就是这样并不是缺陷)
  • 让用户把 cname 改为 0 ,采用直接的方式,这样 SDK 会把 bucketname 和 endpoint 拼成一个 完成域名再上传;
    小结

该问题核心在于

  1. 要对 OSS SDK 的用法熟悉,知道如何进行本地对比用户测试;
  2. 要清楚排查思路知道搜集哪些有价值的信息;
  3. 层级递进,从签名算法着眼,推测到是代码中的使用;
  4. 善用 tcpdump/wireshark 抓包分析请求报文;
相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
1月前
|
存储 人工智能 开发工具
AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
只需要通过向AI助理提问的方式输入您的需求,即可瞬间获得核心流程代码及参数,缩短学习路径、提升开发效率。
1419 4
AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
|
4月前
|
消息中间件 分布式计算 DataWorks
DataWorks产品使用合集之如何使用Python和阿里云SDK读取OSS中的文件
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
6月前
|
安全 Go 开发工具
对象存储OSS产品常见问题之go语言SDK client 和 bucket 并发安全如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。
130 9
|
6月前
|
存储 JSON 前端开发
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
184 0
|
6月前
|
存储 开发工具 对象存储
Javaweb之SpringBootWeb案例之阿里云OSS服务入门的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务入门的详细解析
125 0
|
6月前
|
存储 文字识别 安全
Javaweb之SpringBootWeb案例之阿里云OSS服务的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务的详细解析
185 0
|
4月前
|
机器学习/深度学习 人工智能 专有云
人工智能平台PAI使用问题之怎么将DLC的数据写入到另一个阿里云主账号的OSS中
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
5天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
3月前
|
存储 机器学习/深度学习 弹性计算
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
|
4月前
|
存储 运维 安全
阿里云OSS的优势
【7月更文挑战第19天】阿里云OSS的优势
178 2