forked from TrueCloudLab/frostfs-s3-gw
[#46] authmate: Copy files from cdn-authmate repository to neofs-s3-gw
Copy authmate main file and s3 agent Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
parent
1d9f97374e
commit
50e4eb3b43
2 changed files with 621 additions and 0 deletions
229
authmate/s3.go
Normal file
229
authmate/s3.go
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/nspcc-dev/cdn-sdk"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/bearer"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/hcs"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/s3"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/netmap"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultAuthContainerBasicACL uint32 = 0b00111100100011001000110011001100
|
||||||
|
|
||||||
|
type Agent struct {
|
||||||
|
cli sdk.Client
|
||||||
|
log *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(log *zap.Logger, client sdk.Client) *Agent {
|
||||||
|
return &Agent{log: log, cli: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
IssueSecretOptions struct {
|
||||||
|
ContainerID *container.ID
|
||||||
|
ContainerFriendlyName string
|
||||||
|
NEOFSCreds neofs.Credentials
|
||||||
|
OwnerPrivateKey hcs.PrivateKey
|
||||||
|
GatesPublicKeys []hcs.PublicKey
|
||||||
|
EACLRules []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
ObtainSecretOptions struct {
|
||||||
|
SecretAddress string
|
||||||
|
GatePrivateKey hcs.PrivateKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
issuingResult struct {
|
||||||
|
AccessKeyID string `json:"access_key_id"`
|
||||||
|
SecretAccessKey string `json:"secret_access_key"`
|
||||||
|
OwnerPrivateKey string `json:"owner_private_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
obtainingResult struct {
|
||||||
|
BearerToken *token.BearerToken `json:"-"`
|
||||||
|
SecretAccessKey string `json:"secret_access_key"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Agent) checkContainer(ctx context.Context, cid *container.ID, friendlyName string) (*container.ID, error) {
|
||||||
|
if cid != nil {
|
||||||
|
// check that container exists
|
||||||
|
_, err := a.cli.Container().Get(ctx, cid)
|
||||||
|
return cid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pp, err := buildPlacementPolicy("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to build placement policy")
|
||||||
|
}
|
||||||
|
|
||||||
|
cnr := container.New(
|
||||||
|
container.WithPolicy(pp),
|
||||||
|
container.WithCustomBasicACL(defaultAuthContainerBasicACL),
|
||||||
|
container.WithAttribute(container.AttributeName, friendlyName),
|
||||||
|
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
|
||||||
|
|
||||||
|
return a.cli.Container().Put(ctx, cnr,
|
||||||
|
sdk.ContainerPutAndWait(),
|
||||||
|
sdk.ContainerPutWithTimeout(120*time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
cid *container.ID
|
||||||
|
)
|
||||||
|
|
||||||
|
a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
|
||||||
|
if cid, err = a.checkContainer(ctx, options.ContainerID, options.ContainerFriendlyName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.log.Info("prepare eACL table")
|
||||||
|
|
||||||
|
table, err := buildEACLTable(cid, options.EACLRules)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to build eacl table")
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn, err := buildBearerToken(options.NEOFSCreds.PrivateKey(), options.NEOFSCreds.Owner(), table)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to build bearer token")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.log.Info("store bearer token into NeoFS",
|
||||||
|
zap.Stringer("owner_key", options.NEOFSCreds.Owner()),
|
||||||
|
zap.Stringer("owner_tkn", tkn.Issuer()))
|
||||||
|
|
||||||
|
address, err := bearer.
|
||||||
|
New(a.cli.Object(), options.OwnerPrivateKey).
|
||||||
|
Put(ctx, cid, tkn, options.GatesPublicKeys...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to put bearer token")
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := s3.SecretAccessKey(tkn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get bearer token secret key")
|
||||||
|
}
|
||||||
|
|
||||||
|
ir := &issuingResult{
|
||||||
|
AccessKeyID: address.String(),
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
OwnerPrivateKey: options.OwnerPrivateKey.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
return enc.Encode(ir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
||||||
|
bearerCreds := bearer.New(a.cli.Object(), options.GatePrivateKey)
|
||||||
|
address := object.NewAddress()
|
||||||
|
if err := address.Parse(options.SecretAddress); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to parse secret address")
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn, err := bearerCreds.Get(ctx, address)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get bearer token")
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := s3.SecretAccessKey(tkn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get bearer token secret key")
|
||||||
|
}
|
||||||
|
|
||||||
|
or := &obtainingResult{
|
||||||
|
BearerToken: tkn,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
return enc.Encode(or)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPlacementPolicy(placementRules string) (*netmap.PlacementPolicy, error) {
|
||||||
|
if len(placementRules) != 0 {
|
||||||
|
return policy.Parse(placementRules)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
REP 1 IN X // place one copy of object
|
||||||
|
CBF 1
|
||||||
|
SELECT 2 From * AS X // in container of two nodes
|
||||||
|
*/
|
||||||
|
pp := new(netmap.PlacementPolicy)
|
||||||
|
pp.SetContainerBackupFactor(1)
|
||||||
|
pp.SetReplicas([]*netmap.Replica{newReplica("X", 1)}...)
|
||||||
|
pp.SetSelectors([]*netmap.Selector{newSimpleSelector("X", 2)}...)
|
||||||
|
|
||||||
|
return pp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// selects <count> nodes in container without any additional attributes
|
||||||
|
func newSimpleSelector(name string, count uint32) (s *netmap.Selector) {
|
||||||
|
s = new(netmap.Selector)
|
||||||
|
s.SetCount(count)
|
||||||
|
s.SetFilter("*")
|
||||||
|
s.SetName(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReplica(name string, count uint32) (r *netmap.Replica) {
|
||||||
|
r = new(netmap.Replica)
|
||||||
|
r.SetCount(count)
|
||||||
|
r.SetSelector(name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildEACLTable(cid *container.ID, eaclTable []byte) (*eacl.Table, error) {
|
||||||
|
table := eacl.NewTable()
|
||||||
|
if len(eaclTable) != 0 {
|
||||||
|
return table, table.UnmarshalJSON(eaclTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
record := eacl.NewRecord()
|
||||||
|
record.SetOperation(eacl.OperationGet)
|
||||||
|
record.SetAction(eacl.ActionAllow)
|
||||||
|
// TODO: Change this later.
|
||||||
|
// from := eacl.HeaderFromObject
|
||||||
|
// matcher := eacl.MatchStringEqual
|
||||||
|
// record.AddFilter(from eacl.FilterHeaderType, matcher eacl.Match, name string, value string)
|
||||||
|
eacl.AddFormedTarget(record, eacl.RoleOthers)
|
||||||
|
table.SetCID(cid)
|
||||||
|
table.AddRecord(record)
|
||||||
|
|
||||||
|
return table, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBearerToken(key *ecdsa.PrivateKey, oid *owner.ID, table *eacl.Table) (*token.BearerToken, error) {
|
||||||
|
bearerToken := token.NewBearerToken()
|
||||||
|
bearerToken.SetEACLTable(table)
|
||||||
|
bearerToken.SetOwner(oid)
|
||||||
|
bearerToken.SetLifetime(math.MaxUint64, 0, 0)
|
||||||
|
|
||||||
|
return bearerToken, bearerToken.SignToken(key)
|
||||||
|
}
|
392
cmd/authmate/main.go
Normal file
392
cmd/authmate/main.go
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/cdn-authmate/agents/s3"
|
||||||
|
sdk "github.com/nspcc-dev/cdn-sdk"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/hcs"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/grace"
|
||||||
|
"github.com/nspcc-dev/cdn-sdk/pool"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gateKey struct {
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
poolConnectTimeout = 5 * time.Second
|
||||||
|
poolRequestTimeout = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Build = "now"
|
||||||
|
Version = "dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
neoFSKeyPathFlag string
|
||||||
|
peerAddressFlag string
|
||||||
|
eaclRulesFlag string
|
||||||
|
gatePrivateKeyFlag string
|
||||||
|
secretAddressFlag string
|
||||||
|
ownerPrivateKeyFlag string
|
||||||
|
containerIDFlag string
|
||||||
|
containerFriendlyName string
|
||||||
|
gatesPublicKeysFlag cli.StringSlice
|
||||||
|
gatesKeysCountFlag int
|
||||||
|
logEnabledFlag bool
|
||||||
|
logDebugEnabledFlag bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var zapConfig = zap.Config{
|
||||||
|
Development: true,
|
||||||
|
Encoding: "console",
|
||||||
|
Level: zap.NewAtomicLevelAt(zapcore.FatalLevel),
|
||||||
|
OutputPaths: []string{"stdout"},
|
||||||
|
EncoderConfig: zapcore.EncoderConfig{
|
||||||
|
MessageKey: "message",
|
||||||
|
LevelKey: "level",
|
||||||
|
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||||
|
TimeKey: "time",
|
||||||
|
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||||
|
CallerKey: "caller",
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepare() (context.Context, *zap.Logger) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
log = zap.NewNop()
|
||||||
|
)
|
||||||
|
|
||||||
|
if !logEnabledFlag {
|
||||||
|
return grace.Context(log), log
|
||||||
|
} else if logDebugEnabledFlag {
|
||||||
|
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if log, err = zapConfig.Build(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return grace.Context(log), log
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := &cli.App{
|
||||||
|
Name: "NeoFS gate authentication manager",
|
||||||
|
Usage: "Helps manage delegated access via gates to data stored in NeoFS network",
|
||||||
|
Version: Version,
|
||||||
|
Flags: appFlags(),
|
||||||
|
Commands: appCommands(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err)
|
||||||
|
os.Exit(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appFlags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "with-log",
|
||||||
|
Usage: "Enable logger",
|
||||||
|
Destination: &logEnabledFlag,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
Usage: "Enable debug logger level",
|
||||||
|
Destination: &logDebugEnabledFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appCommands() []*cli.Command {
|
||||||
|
return []*cli.Command{
|
||||||
|
issueSecret(),
|
||||||
|
obtainSecret(),
|
||||||
|
generateKeys(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateGatesKeys(count int) ([]hcs.Credentials, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
res = make([]hcs.Credentials, count, count)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
if res[i], err = hcs.Generate(rand.Reader); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateKeys() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "generate-keys",
|
||||||
|
Usage: "Generate key pairs for gates",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "count",
|
||||||
|
Usage: "number of x25519 key pairs to generate",
|
||||||
|
Value: 1,
|
||||||
|
Destination: &gatesKeysCountFlag,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
_, log := prepare()
|
||||||
|
|
||||||
|
log.Info("start generating x25519 keys")
|
||||||
|
|
||||||
|
csl, err := generateGatesKeys(gatesKeysCountFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to create key pairs of gates: %s", err), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("generated x25519 keys")
|
||||||
|
|
||||||
|
gatesKeys := make([]gateKey, len(csl), len(csl))
|
||||||
|
for i, cs := range csl {
|
||||||
|
privateKey, publicKey := cs.PrivateKey().String(), cs.PublicKey().String()
|
||||||
|
gatesKeys[i] = gateKey{PrivateKey: privateKey, PublicKey: publicKey}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := json.MarshalIndent(gatesKeys, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to marshal key pairs of gates: %s", err), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(keys))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueSecret() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "issue-secret",
|
||||||
|
Usage: "Issue a secret in NeoFS network",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "neofs-key",
|
||||||
|
Value: "",
|
||||||
|
Usage: "path to owner's neofs private ecdsa key",
|
||||||
|
Required: true,
|
||||||
|
Destination: &neoFSKeyPathFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "peer",
|
||||||
|
Value: "",
|
||||||
|
Usage: "address of a neofs peer to connect to",
|
||||||
|
Required: true,
|
||||||
|
Destination: &peerAddressFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "rules",
|
||||||
|
Usage: "eacl rules as plain json string",
|
||||||
|
Required: false,
|
||||||
|
Destination: &eaclRulesFlag,
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "gate-public-key",
|
||||||
|
Usage: "public x25519 key of a gate (use flags repeatedly for multiple gates)",
|
||||||
|
Required: true,
|
||||||
|
Destination: &gatesPublicKeysFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner-private-key",
|
||||||
|
Usage: "owner's private x25519 key",
|
||||||
|
Required: false,
|
||||||
|
Destination: &ownerPrivateKeyFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "container-id",
|
||||||
|
Usage: "auth container id to put the secret into",
|
||||||
|
Required: false,
|
||||||
|
Destination: &containerIDFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "container-friendly-name",
|
||||||
|
Usage: "friendly name of auth container to put the secret into",
|
||||||
|
Required: false,
|
||||||
|
Destination: &containerFriendlyName,
|
||||||
|
Value: "auth-container",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
ctx, log := prepare()
|
||||||
|
|
||||||
|
neofsCreds, err := neofs.New(neoFSKeyPathFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
client, err := createSDKClient(ctx, log, neofsCreds, peerAddressFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
agent := s3.New(log, client)
|
||||||
|
var cid *container.ID
|
||||||
|
if len(containerIDFlag) > 0 {
|
||||||
|
cid = container.NewID()
|
||||||
|
if err := cid.Parse(containerIDFlag); err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to parse auth container id: %s", err), 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var owner hcs.Credentials
|
||||||
|
if owner, err = fetchHCSCredentials(ownerPrivateKeyFlag); err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gatesPublicKeys []hcs.PublicKey
|
||||||
|
for _, key := range gatesPublicKeysFlag.Value() {
|
||||||
|
gpk, err := hcs.LoadPublicKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to load gate's public key: %s", err), 5)
|
||||||
|
}
|
||||||
|
gatesPublicKeys = append(gatesPublicKeys, gpk)
|
||||||
|
}
|
||||||
|
|
||||||
|
issueSecretOptions := &s3.IssueSecretOptions{
|
||||||
|
ContainerID: cid,
|
||||||
|
ContainerFriendlyName: containerFriendlyName,
|
||||||
|
NEOFSCreds: neofsCreds,
|
||||||
|
OwnerPrivateKey: owner.PrivateKey(),
|
||||||
|
GatesPublicKeys: gatesPublicKeys,
|
||||||
|
EACLRules: []byte(eaclRulesFlag),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to issue secret: %s", err), 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func obtainSecret() *cli.Command {
|
||||||
|
command := &cli.Command{
|
||||||
|
Name: "obtain-secret",
|
||||||
|
Usage: "Obtain a secret from NeoFS network",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "neofs-key",
|
||||||
|
Value: "",
|
||||||
|
Usage: "path to owner's neofs private ecdsa key",
|
||||||
|
Required: true,
|
||||||
|
Destination: &neoFSKeyPathFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "peer",
|
||||||
|
Value: "",
|
||||||
|
Usage: "address of neofs peer to connect to",
|
||||||
|
Required: true,
|
||||||
|
Destination: &peerAddressFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "gate-private-key",
|
||||||
|
Usage: "gate's private x25519 key",
|
||||||
|
Required: true,
|
||||||
|
Destination: &gatePrivateKeyFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "secret-address",
|
||||||
|
Usage: "address of a secret (i.e. access key id for s3)",
|
||||||
|
Required: true,
|
||||||
|
Destination: &secretAddressFlag,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
ctx, log := prepare()
|
||||||
|
|
||||||
|
neofsCreds, err := neofs.New(neoFSKeyPathFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
client, err := createSDKClient(ctx, log, neofsCreds, peerAddressFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
agent := s3.New(log, client)
|
||||||
|
|
||||||
|
var _ = agent
|
||||||
|
|
||||||
|
gateCreds, err := hcs.NewCredentials(gatePrivateKeyFlag)
|
||||||
|
if err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
obtainSecretOptions := &s3.ObtainSecretOptions{
|
||||||
|
SecretAddress: secretAddressFlag,
|
||||||
|
GatePrivateKey: gateCreds.PrivateKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = agent.ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil {
|
||||||
|
return cli.Exit(fmt.Sprintf("failed to obtain secret: %s", err), 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return command
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchHCSCredentials(val string) (hcs.Credentials, error) {
|
||||||
|
if val == "" {
|
||||||
|
return hcs.Generate(rand.Reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hcs.NewCredentials(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSDKClient(ctx context.Context, log *zap.Logger, neofsCreds neofs.Credentials, peerAddress string) (sdk.Client, error) {
|
||||||
|
log.Debug("prepare connection pool")
|
||||||
|
|
||||||
|
p, err := pool.New(ctx,
|
||||||
|
pool.WithLogger(log),
|
||||||
|
pool.WithAddress(peerAddress),
|
||||||
|
pool.WithCredentials(neofsCreds),
|
||||||
|
pool.WithAPIPreparer(sdk.APIPreparer),
|
||||||
|
pool.WithConnectTimeout(poolConnectTimeout),
|
||||||
|
pool.WithRequestTimeout(poolRequestTimeout))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to create connection pool")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("prepare sdk client")
|
||||||
|
|
||||||
|
return sdk.New(ctx,
|
||||||
|
sdk.WithLogger(log),
|
||||||
|
sdk.WithCredentials(neofsCreds),
|
||||||
|
sdk.WithConnectionPool(p),
|
||||||
|
sdk.WithAPIPreparer(sdk.APIPreparer))
|
||||||
|
}
|
Loading…
Reference in a new issue