diff --git a/CHANGELOG.md b/CHANGELOG.md index cb8ac958..46037248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This document outlines major changes between releases. - Add `namespace` label to billing metrics (#271) - Support policy-engine (#257) - Support `policy` contract (#259) +- Support `proxy` contract (#287) ### Changed - Generalise config param `use_default_xmlns_for_complete_multipart` to `use_default_xmlns` so that use default xmlns for all requests (#221) diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index 08d74786..23b59dff 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -438,9 +438,10 @@ func (a *App) initMetrics() { func (a *App) initFrostfsID(ctx context.Context) { var err error a.frostfsid, err = frostfsid.New(ctx, frostfsid.Config{ - RPCAddress: a.cfg.GetString(cfgRPCEndpoint), - Contract: a.cfg.GetString(cfgFrostfsIDContract), - Key: a.key, + RPCAddress: a.cfg.GetString(cfgRPCEndpoint), + Contract: a.cfg.GetString(cfgFrostfsIDContract), + ProxyContract: a.cfg.GetString(cfgProxyContract), + Key: a.key, }) if err != nil { a.log.Fatal(logs.InitFrostfsIDContractFailed, zap.Error(err)) @@ -455,9 +456,10 @@ func (a *App) initPolicyStorage(ctx context.Context) { if a.cfg.GetBool(cfgPolicyEnabled) { policyContract, err = contract.New(ctx, contract.Config{ - RPCAddress: a.cfg.GetString(cfgRPCEndpoint), - Contract: a.cfg.GetString(cfgPolicyContract), - Key: a.key, + RPCAddress: a.cfg.GetString(cfgRPCEndpoint), + Contract: a.cfg.GetString(cfgPolicyContract), + ProxyContract: a.cfg.GetString(cfgProxyContract), + Key: a.key, }) if err != nil { a.log.Fatal(logs.InitPolicyContractFailed, zap.Error(err)) diff --git a/cmd/s3-gw/app_settings.go b/cmd/s3-gw/app_settings.go index fcc43c23..ed1c53c5 100644 --- a/cmd/s3-gw/app_settings.go +++ b/cmd/s3-gw/app_settings.go @@ -213,6 +213,9 @@ const ( // Settings. cfgPolicyEnabled = "policy.enabled" cfgPolicyContract = "policy.contract" + // Proxy. + cfgProxyContract = "proxy.contract" + // envPrefix is an environment variables prefix used for configuration. envPrefix = "S3_GW" ) @@ -702,6 +705,9 @@ func newSettings() *viper.Viper { v.SetDefault(cfgPolicyContract, "policy.frostfs") v.SetDefault(cfgPolicyEnabled, true) + // proxy + v.SetDefault(cfgProxyContract, "proxy.frostfs") + // resolve v.SetDefault(cfgResolveNamespaceHeader, defaultNamespaceHeader) diff --git a/config/config.env b/config/config.env index d1790257..037a4262 100644 --- a/config/config.env +++ b/config/config.env @@ -204,6 +204,10 @@ S3_GW_POLICY_ENABLED=true # Policy contract hash (LE) or name in NNS. S3_GW_POLICY_CONTRACT=policy.frostfs +# Proxy contract configuration. To enable this functionality the `rpc_endpoint` param must be also set. +# Proxy contract hash (LE) or name in NNS. +S3_GW_PROXY_CONTRACT=proxy.frostfs + # Namespaces configuration S3_GW_NAMESPACES_CONFIG=namespaces.json diff --git a/config/config.yaml b/config/config.yaml index abbc27d2..06eb9628 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -241,5 +241,10 @@ policy: # Policy contract hash (LE) or name in NNS. contract: policy.frostfs +# Proxy contract configuration. To enable this functionality the `rpc_endpoint` param must be also set. +proxy: + # Proxy contract hash (LE) or name in NNS. + contract: proxy.frostfs + namespaces: config: namespaces.json diff --git a/docs/configuration.md b/docs/configuration.md index ec6b34f1..e7d5adf1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -191,6 +191,7 @@ There are some custom types used for brevity: | `web` | [Web server configuration](#web-section) | | `frostfsid` | [FrostfsID configuration](#frostfsid-section) | | `policy` | [Policy contract configuration](#policy-section) | +| `proxy` | [Proxy contract configuration](#proxy-section) | | `namespaces` | [Namespaces configuration](#namespaces-section) | ### General section @@ -662,6 +663,19 @@ policy: | `enabled` | `bool` | no | true | Enables using policies from Policy contract to check permissions. | | `contract` | `string` | no | policy.frostfs | Policy contract hash (LE) or name in NNS. | +# `proxy` section + +Proxy contract configuration. To enable this functionality the `rpc_endpoint` param must be also set. + +```yaml +proxy: + contract: proxy.frostfs +``` + +| Parameter | Type | SIGHUP reload | Default value | Description | +|------------|----------|---------------|-----------------|------------------------------------------| +| `contract` | `string` | no | `proxy.frostfs` | Proxy contract hash (LE) or name in NNS. | + # `namespaces` section Namespaces configuration. diff --git a/internal/frostfs/policy/contract/contract.go b/internal/frostfs/policy/contract/contract.go index 94c72992..2296e254 100644 --- a/internal/frostfs/policy/contract/contract.go +++ b/internal/frostfs/policy/contract/contract.go @@ -9,9 +9,11 @@ import ( policyclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/policy" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy" frostfsutil "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/util" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" ) @@ -28,6 +30,9 @@ type Config struct { // Contract is hash of contract or its name in NNS. Contract string + // ProxyContract is hash of proxy contract or its name in NNS to interact with policy. + ProxyContract string + // Key is used to interact with policy contract. // If this is nil than random key will be generated. Key *keys.PrivateKey @@ -54,8 +59,12 @@ func New(ctx context.Context, cfg Config) (*Client, error) { return nil, fmt.Errorf("create policy rpc client: %w", err) } - acc := wallet.NewAccountFromPrivateKey(key) - act, err := actor.NewSimple(rpcCli, acc) + proxyContractHash, err := frostfsutil.ResolveContractHash(cfg.ProxyContract, cfg.RPCAddress) + if err != nil { + return nil, fmt.Errorf("resolve frostfs contract hash: %w", err) + } + + act, err := actor.New(rpcCli, getSigners(key, proxyContractHash, contractHash)) if err != nil { return nil, fmt.Errorf("create new actor: %w", err) } @@ -66,6 +75,28 @@ func New(ctx context.Context, cfg Config) (*Client, error) { }, nil } +func getSigners(key *keys.PrivateKey, proxyHash, contractHash util.Uint160) []actor.SignerAccount { + acc := wallet.NewAccountFromPrivateKey(key) + + return []actor.SignerAccount{ + { + Signer: transaction.Signer{ + Account: proxyHash, + Scopes: transaction.CustomContracts, + AllowedContracts: []util.Uint160{contractHash}, + }, + Account: notary.FakeContractAccount(proxyHash), + }, + { + Signer: transaction.Signer{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }, + Account: acc, + }, + } +} + func (c *Client) AddChain(kind policycontract.Kind, entity string, name []byte, chain []byte) (util.Uint256, uint32, error) { return c.policyContract.AddChain(big.NewInt(int64(kind)), entity, name, chain) }