"partNumber",
"policy",
"policyStatus",
+ "publicAccessBlock",
"requestPayment",
"response-cache-control",
"response-content-disposition",
(name.compare("tagging") == 0) ||
(name.compare("append") == 0) ||
(name.compare("position") == 0) ||
- (name.compare("policyStatus") == 0)) {
+ (name.compare("policyStatus") == 0) ||
+ (name.compare("publicAccessBlock") == 0)) {
sub_resources[name] = val;
} else if (name[0] == 'r') { // root of all evil
if ((name.compare("response-content-type") == 0) ||
/* IAM Policy */
#define RGW_ATTR_IAM_POLICY RGW_ATTR_PREFIX "iam-policy"
#define RGW_ATTR_USER_POLICY RGW_ATTR_PREFIX "user-policy"
+#define RGW_ATTR_PUBLIC_ACCESS RGW_ATTR_PREFIX "public-access"
/* RGW File Attributes */
#define RGW_ATTR_UNIX_KEY1 RGW_ATTR_PREFIX "unix-key1"
#define RGW_ATTR_CRYPT_KEYID RGW_ATTR_CRYPT_PREFIX "keyid"
#define RGW_ATTR_CRYPT_KEYSEL RGW_ATTR_CRYPT_PREFIX "keysel"
+
#define RGW_FORMAT_PLAIN 0
#define RGW_FORMAT_XML 1
#define RGW_FORMAT_JSON 2
RGW_OP_DELETE_BUCKET_REPLICATION,
RGW_OP_GET_BUCKET_POLICY_STATUS
+
+ /* public access */
+ RGW_OP_GET_BUCKET_POLICY_STATUS,
+ RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK,
+ RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK,
+ RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK,
};
class RGWAccessControlPolicy;
{ "s3:GetBucketLogging", s3GetBucketLogging },
{ "s3:GetBucketNotification", s3GetBucketNotification },
{ "s3:GetBucketPolicy", s3GetBucketPolicy },
+ { "s3:GetBucketPublicAccessBlock", s3GetBucketPublicAccessBlock },
{ "s3:GetBucketRequestPayment", s3GetBucketRequestPayment },
{ "s3:GetBucketTagging", s3GetBucketTagging },
{ "s3:GetBucketVersioning", s3GetBucketVersioning },
{ "s3:PutObjectRetention", s3PutObjectRetention },
{ "s3:PutObjectLegalHold", s3PutObjectLegalHold },
{ "s3:BypassGovernanceRetention", s3BypassGovernanceRetention },
+ { "s3:PutBucketPublicAccessBlock", s3PutBucketPublicAccessBlock },
{ "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
{ "s3:RestoreObject", s3RestoreObject },
{ "iam:PutUserPolicy", iamPutUserPolicy },
static constexpr std::uint64_t s3GetObjectLegalHold = 59;
static constexpr std::uint64_t s3BypassGovernanceRetention = 60;
static constexpr std::uint64_t s3GetBucketPolicyStatus = 61;
-static constexpr std::uint64_t s3All = 62;
+static constexpr std::uint64_t s3PutPublicAccessBlock = 62;
+static constexpr std::uint64_t s3GetPublicAccessBlock = 63;
+static constexpr std::uint64_t s3DeletePublicAccessBlock = 64;
+static constexpr std::uint64_t s3GetBucketPublicAccessBlock = 65;
+static constexpr std::uint64_t s3PutBucketPublicAccessBlock = 66;
+static constexpr std::uint64_t s3DeleteBucketPublicAccessBlock = 67;
+static constexpr std::uint64_t s3All = 68;
static constexpr std::uint64_t iamPutUserPolicy = s3All + 1;
static constexpr std::uint64_t iamGetUserPolicy = s3All + 2;
case s3GetObjectVersionAcl:
case s3GetReplicationConfiguration:
case s3GetBucketObjectLockConfiguration:
+ case s3GetBucketPublicAccessBlock:
return RGW_PERM_READ_ACP;
case s3DeleteBucketPolicy:
case s3PutObjectVersionAcl:
case s3PutReplicationConfiguration:
case s3PutBucketObjectLockConfiguration:
+ case s3PutBucketPublicAccessBlock:
return RGW_PERM_WRITE_ACP;
case s3All:
if (s->iam_policy)
isPublic |= rgw::IAM::IsPublic(*s->iam_policy);
}
+
+int RGWPutBucketPublicAccessBlock::verify_permission()
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+int RGWPutBucketPublicAccessBlock::get_params()
+{
+ const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+ std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
+ return op_ret;
+}
+
+void RGWPutBucketPublicAccessBlock::execute()
+{
+ RGWXMLDecoder::XMLParser parser;
+ if (!parser.init()) {
+ ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+ op_ret = -EINVAL;
+ return;
+ }
+
+ op_ret = get_params();
+ if (op_ret < 0)
+ return;
+
+ if (!parser.parse(data.c_str(), data.length(), 1)) {
+ ldpp_dout(this,0) << "ERROR: malformed XML" << dendl;
+ ldpp_dout(this,20) << "xml: " << data.c_str() << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ try {
+ RGWXMLDecoder::decode_xml("PublicAccessBlockConfiguration", access_conf, &parser, true);
+ } catch (RGWXMLDecoder::err &err) {
+ ldout(s->cct, 5) << "unexpected xml:" << err << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ if (!store->svc()->zone->is_meta_master()) {
+ op_ret = forward_request_to_master(s, NULL, store, data, nullptr);
+ if (op_ret < 0) {
+ ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+ }
+
+ bufferlist bl;
+ access_conf.encode(bl);
+ op_ret = retry_raced_bucket_write(store->getRados(), s, [this, &bl] {
+ map<string, bufferlist> attrs = s->bucket_attrs;
+ attrs[RGW_ATTR_PUBLIC_ACCESS] = bl;
+ return store->ctl()->bucket->set_bucket_instance_attrs(s->bucket_info, attrs, &s->bucket_info.objv_tracker, s->yield);
+ });
+
+}
+
+int RGWGetBucketPublicAccessBlock::verify_permission()
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWGetBucketPublicAccessBlock::execute()
+{
+ auto attrs = s->bucket_attrs;
+ if (auto aiter = attrs.find(RGW_ATTR_PUBLIC_ACCESS);
+ aiter == attrs.end()) {
+ ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
+ << s->bucket_name << dendl;
+ // return the default;
+ return;
+ } else {
+ bufferlist::const_iterator iter{&aiter->second};
+ try {
+ access_conf.decode(iter);
+ } catch (const buffer::error& e) {
+ ldout(s->cct, 0) << __func__ << "decode object legal hold config failed" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ }
+}
+
+
+void RGWDeleteBucketPublicAccessBlock::send_response()
+{
+ if (op_ret) {
+ set_req_state_err(s, op_ret);
+ }
+ dump_errno(s);
+ end_header(s);
+}
+
+int RGWDeleteBucketPublicAccessBlock::verify_permission()
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWDeleteBucketPublicAccessBlock::execute()
+{
+ op_ret = retry_raced_bucket_write(store->getRados(), s, [this] {
+ auto attrs = s->bucket_attrs;
+ attrs.erase(RGW_ATTR_PUBLIC_ACCESS);
+ op_ret = store->ctl()->bucket->set_bucket_instance_attrs(s->bucket_info, attrs,
+ &s->bucket_info.objv_tracker,
+ s->yield);
+ return op_ret;
+ });
+}
#include "rgw_object_lock.h"
#include "cls/lock/cls_lock_client.h"
#include "cls/rgw/cls_rgw_client.h"
+#include "rgw_public_access.h"
#include "services/svc_sys_obj.h"
#include "services/svc_tier_rados.h"
dmc::client_id dmclock_client() override { return dmc::client_id::metadata; }
};
+class RGWPutBucketPublicAccessBlock : public RGWOp {
+protected:
+ bufferlist data;
+ rgw::IAM::PublicAccessConfiguration access_conf;
+public:
+ int verify_permission() override;
+ const char* name() const override { return "put_bucket_public_access_block";}
+ virtual RGWOpType get_type() override { return RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK; }
+ virtual uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+ int get_params();
+ void execute() override;
+ dmc::client_id dmclock_client() override { return dmc::client_id::metadata; }
+};
+
+class RGWGetBucketPublicAccessBlock : public RGWOp {
+protected:
+ rgw::IAM::PublicAccessConfiguration access_conf;
+public:
+ int verify_permission() override;
+ const char* name() const override { return "get_bucket_public_access_block";}
+ virtual RGWOpType get_type() override { return RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK; }
+ virtual uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
+ int get_params();
+ void execute() override;
+ dmc::client_id dmclock_client() override { return dmc::client_id::metadata; }
+};
+
+class RGWDeleteBucketPublicAccessBlock : public RGWOp {
+protected:
+ rgw::IAM::PublicAccessConfiguration access_conf;
+public:
+ int verify_permission() override;
+ const char* name() const override { return "delete_bucket_public_access_block";}
+ virtual RGWOpType get_type() override { return RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK; }
+ virtual uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+ int get_params();
+ void execute() override;
+ void send_response() override;
+ dmc::client_id dmclock_client() override { return dmc::client_id::metadata; }
+};
+
static inline int parse_value_and_bound(
const string &input,
int &output,
-#include "rgw_public_acess.h"
+#include "rgw_public_access.h"
#include "rgw_xml.h"
namespace rgw::IAM {
void PublicAccessConfiguration::dump_xml(Formatter *f) const {
Formatter::ObjectSection os(*f, "BlockPublicAccessConfiguration");
- encode_xml("BlockPublicAcls", BlockPublicAcls, f);
- encode_xml("IgnorePublicAcls", IgnorePublicAcls, f);
- encode_xml("BlockPublicPolicy", BlockPublicPolicy, f);
- encode_xml("RestrictPublicBuckets", RestrictPublicBuckets, f);
+ // AWS spec mentions the values to be ALL CAPs, but clients will not
+ // understand this or a mixed case like it is supposed to, hence the need to
+ // manually encode here
+ auto bool_val = [](bool b) -> auto { return b ? "true": "false"; };
+
+ f->dump_string("BlockPublicAcls", bool_val(BlockPublicAcls));
+ f->dump_string("IgnorePublicAcls", bool_val(IgnorePublicAcls));
+ f->dump_string("BlockPublicPolicy", bool_val(BlockPublicPolicy));
+ f->dump_string("RestrictPublicBuckets", bool_val(RestrictPublicBuckets));
+}
+
+
+ostream& operator<< (ostream& os, const PublicAccessConfiguration& access_conf)
+{
+ os << std::boolalpha
+ << "BlockPublicAcls: " << access_conf.get_block_public_acls() << std::endl
+ << "IgnorePublicAcls: " << access_conf.get_ignore_public_acls() << std::endl
+ << "BlockPublicPolicy" << access_conf.get_block_public_policy() << std::endl
+ << "RestrictPublicBuckets" << access_conf.get_restrict_public_buckets() << std::endl;
+
+ return os;
}
} // namespace rgw::IAM
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 SUSE LLC
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#pragma once
+#include <include/types.h>
+
+class XMLObj;
+
+namespace rgw::IAM {
+
+class PublicAccessConfiguration {
+ bool BlockPublicAcls;
+ bool IgnorePublicAcls;
+ bool BlockPublicPolicy;
+ bool RestrictPublicBuckets;
+ public:
+ PublicAccessConfiguration():
+ BlockPublicAcls(true), IgnorePublicAcls(false),
+ BlockPublicPolicy(true), RestrictPublicBuckets(false)
+ {}
+
+ auto get_block_public_acls() const {
+ return BlockPublicAcls;
+ }
+ auto get_ignore_public_acls() const {
+ return IgnorePublicAcls;
+ }
+ auto get_block_public_policy() const {
+ return BlockPublicPolicy;
+ }
+ auto get_restrict_public_buckets() const {
+ return RestrictPublicBuckets;
+ }
+
+ void encode(ceph::bufferlist& bl) const {
+ ENCODE_START(1,1, bl);
+ encode(BlockPublicAcls, bl);
+ encode(IgnorePublicAcls, bl);
+ encode(BlockPublicPolicy, bl);
+ encode(RestrictPublicBuckets, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::bufferlist::const_iterator& bl) {
+ DECODE_START(1,bl);
+ decode(BlockPublicAcls, bl);
+ decode(IgnorePublicAcls, bl);
+ decode(BlockPublicPolicy, bl);
+ decode(RestrictPublicBuckets, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void decode_xml(XMLObj *obj);
+ void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(PublicAccessConfiguration)
+ostream& operator<< (ostream& os, const PublicAccessConfiguration& access_conf);
+} // namespace rgw::IAM
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab ft=cpp
-
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2019 SUSE LLC
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation. See file COPYING.
- *
- */
-
-#pragma once
-#include <include/types.h>
-
-class XMLObj;
-
-namespace rgw::IAM {
-
-class PublicAccessConfiguration {
- bool BlockPublicAcls;
- bool IgnorePublicAcls;
- bool BlockPublicPolicy;
- bool RestrictPublicBuckets;
- public:
- PublicAccessConfiguration():
- BlockPublicAcls(true), IgnorePublicAcls(false),
- BlockPublicPolicy(true), RestrictPublicBuckets(false)
- {}
-
- auto get_block_public_acls() const {
- return BlockPublicAcls;
- }
- auto get_ignore_public_acls() const {
- return IgnorePublicAcls;
- }
- auto get_block_public_policy() const {
- return BlockPublicPolicy;
- }
- auto get_restrict_public_buckets() const {
- return RestrictPublicBuckets;
- }
-
- void encode(ceph::bufferlist& bl) const {
- ENCODE_START(1,1, bl);
- encode(BlockPublicAcls, bl);
- encode(IgnorePublicAcls, bl);
- encode(BlockPublicPolicy, bl);
- encode(RestrictPublicBuckets, bl);
- ENCODE_FINISH(bl);
- }
-
- void decode(ceph::bufferlist::const_iterator& bl) {
- DECODE_START(1,bl);
- decode(BlockPublicAcls, bl);
- decode(IgnorePublicAcls, bl);
- decode(BlockPublicPolicy, bl);
- decode(RestrictPublicBuckets, bl);
- DECODE_FINISH(bl);
- }
-
- void decode_xml(XMLObj *obj);
- void dump_xml(Formatter *f) const;
-};
-WRITE_CLASS_ENCODER(PublicAccessConfiguration)
-
-} // namespace rgw::IAM
}
+void RGWPutBucketPublicAccessBlock_ObjStore_S3::send_response()
+{
+ if (op_ret) {
+ set_req_state_err(s, op_ret);
+ }
+ dump_errno(s);
+ end_header(s);
+}
+
+void RGWGetBucketPublicAccessBlock_ObjStore_S3::send_response()
+{
+ if (op_ret) {
+ set_req_state_err(s, op_ret);
+ }
+ dump_errno(s);
+ end_header(s, this, "application/xml");
+ dump_start(s);
+
+ access_conf.dump_xml(s->formatter);
+ rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
RGWOp *RGWHandler_REST_Service_S3::op_get()
{
if (is_usage_op()) {
return new RGWGetBucketReplication_ObjStore_S3;
} else if (is_policy_status_op()) {
return new RGWGetBucketPolicyStatus_ObjStore_S3;
+ } else if (is_block_public_access_op()) {
+ return new RGWGetBucketPublicAccessBlock_ObjStore_S3;
}
return get_obj_op(true);
}
}
return new RGWSetBucketWebsite_ObjStore_S3;
}
-
if (is_tagging_op()) {
return new RGWPutBucketTags_ObjStore_S3;
} else if (is_acl_op()) {
}
return new RGWPutBucketReplication_ObjStore_S3;
+ } else if (is_block_public_access_op()) {
+ return new RGWPutBucketPublicAccessBlock_ObjStore_S3;
}
return new RGWCreateBucket_ObjStore_S3;
}
return RGWHandler_REST_PSNotifs_S3::create_delete_op();
} else if (is_replication_op()) {
return new RGWDeleteBucketReplication_ObjStore_S3;
+ } else if (is_block_public_access_op()) {
+ return new RGWDeleteBucketPublicAccessBlock;
}
if (s->info.args.sub_resource_exists("website")) {
case RGW_OP_PUT_OBJ_LEGAL_HOLD:
case RGW_STS_GET_SESSION_TOKEN:
case RGW_STS_ASSUME_ROLE:
+ case RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK:
+ case RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK:
+ case RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK:
break;
default:
dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
void send_response() override;
};
+class RGWPutBucketPublicAccessBlock_ObjStore_S3 : public RGWPutBucketPublicAccessBlock {
+public:
+ void send_response() override;
+};
+
+class RGWGetBucketPublicAccessBlock_ObjStore_S3 : public RGWGetBucketPublicAccessBlock {
+public:
+ void send_response() override;
+};
+
class RGW_Auth_S3 {
public:
static int authorize(const DoutPrefixProvider *dpp,
bool is_policy_status_op() {
return s->info.args.exists("policyStatus");
}
+ bool is_block_public_access_op() {
+ return s->info.args.exists("publicAccessBlock");
+ }
RGWOp *get_obj_op(bool get_data) const;
RGWOp *op_get() override;