cmd/uplink: add --tls flag to share command

adds an additional flag to return an additional TXT record that will
enable TLS on custom domains with Linksharing.

Closes #5623

Change-Id: I941616362d7dcd9aec20dfd10346e483021516a4
This commit is contained in:
Sean Harvey 2023-03-31 15:59:10 +13:00
parent b44852bb02
commit 33c3e3176f
No known key found for this signature in database
GPG Key ID: D917C00695250311
2 changed files with 106 additions and 4 deletions

View File

@ -31,6 +31,7 @@ type cmdShare struct {
register bool register bool
url bool url bool
dns string dns string
tls bool
authService string authService string
caCert string caCert string
public bool public bool
@ -52,7 +53,10 @@ func (c *cmdShare) Setup(params clingy.Parameters) {
c.url = params.Flag("url", "If true, returns a url for the shared path. implies --register and --public", false, c.url = params.Flag("url", "If true, returns a url for the shared path. implies --register and --public", false,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Transform(strconv.ParseBool), clingy.Boolean,
).(bool) ).(bool)
c.dns = params.Flag("dns", "Specify your custom hostname. if set, returns dns settings for web hosting. implies --register and --public", "").(string) c.dns = params.Flag("dns", "Specify your custom domain. if set, returns DNS settings for web hosting. implies --register and --public", "").(string)
c.tls = params.Flag("tls", "Return an additional TXT record to secure your domain (Pro Accounts only.) implies --dns and --public", false,
clingy.Transform(strconv.ParseBool), clingy.Boolean,
).(bool)
c.authService = params.Flag("auth-service", "URL for shared auth service", "https://auth.storjshare.io").(string) c.authService = params.Flag("auth-service", "URL for shared auth service", "https://auth.storjshare.io").(string)
c.public = params.Flag("public", "If true, the access will be public. --dns and --url override this", false, c.public = params.Flag("public", "If true, the access will be public. --dns and --url override this", false,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Transform(strconv.ParseBool), clingy.Boolean,
@ -77,7 +81,11 @@ func (c *cmdShare) Execute(ctx context.Context) error {
return err return err
} }
c.public = c.public || c.url || c.dns != "" if c.tls && c.dns == "" {
return errs.New("You must specify your custom domain with --dns")
}
c.public = c.public || c.url || c.dns != "" || c.tls
if c.public { if c.public {
c.register = true c.register = true
@ -136,7 +144,7 @@ func (c *cmdShare) Execute(ctx context.Context) error {
return errs.New("will only generate DNS entries with readonly restrictions") return errs.New("will only generate DNS entries with readonly restrictions")
} }
err = createDNS(ctx, credentials.AccessKeyID, c.ap.prefixes, c.baseURL, c.dns) err = createDNS(ctx, credentials.AccessKeyID, c.ap.prefixes, c.baseURL, c.dns, c.tls)
if err != nil { if err != nil {
return err return err
} }
@ -250,7 +258,7 @@ func createURL(ctx context.Context, accessKeyID string, prefixes []uplink.ShareP
} }
// Creates dns record info for allowed path prefixes. // Creates dns record info for allowed path prefixes.
func createDNS(ctx context.Context, accessKey string, prefixes []uplink.SharePrefix, baseURL, dns string) (err error) { func createDNS(ctx context.Context, accessKey string, prefixes []uplink.SharePrefix, baseURL, dns string, tls bool) (err error) {
if len(prefixes) == 0 { if len(prefixes) == 0 {
return errs.New("need at least a bucket to create DNS records") return errs.New("need at least a bucket to create DNS records")
} }
@ -277,6 +285,9 @@ func createDNS(ctx context.Context, accessKey string, prefixes []uplink.SharePre
fmt.Fprintf(clingy.Stdout(ctx), "%s \tIN\tCNAME\t%s.\n", dns, CNAME.Host) fmt.Fprintf(clingy.Stdout(ctx), "%s \tIN\tCNAME\t%s.\n", dns, CNAME.Host)
fmt.Fprintln(clingy.Stdout(ctx), printStorjRoot) fmt.Fprintln(clingy.Stdout(ctx), printStorjRoot)
fmt.Fprintf(clingy.Stdout(ctx), "txt-%s\tIN\tTXT \tstorj-access:%s\n", dns, accessKey) fmt.Fprintf(clingy.Stdout(ctx), "txt-%s\tIN\tTXT \tstorj-access:%s\n", dns, accessKey)
if tls {
fmt.Fprintf(clingy.Stdout(ctx), "txt-%s\tIN\tTXT \tstorj-tls:true\n", dns)
}
return nil return nil
} }

View File

@ -4,13 +4,24 @@
package main package main
import ( import (
"context"
"net"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"storj.io/common/grant"
"storj.io/common/macaroon"
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/common/testcontext"
"storj.io/drpc/drpcmux"
"storj.io/drpc/drpcserver"
"storj.io/storj/cmd/uplink/ultest" "storj.io/storj/cmd/uplink/ultest"
) )
const testAPIKey = "13Yqe3oHi5dcnGhMu2ru3cmePC9iEYv6nDrYMbLRh4wre1KtVA9SFwLNAuuvWwc43b9swRsrfsnrbuTHQ6TJKVt4LjGnaARN9PhxJEu"
func TestShare(t *testing.T) { func TestShare(t *testing.T) {
t.Run("share requires prefix", func(t *testing.T) { t.Run("share requires prefix", func(t *testing.T) {
ultest.Setup(commands).Fail(t, "share") ultest.Setup(commands).Fail(t, "share")
@ -134,4 +145,84 @@ func TestShare(t *testing.T) {
Access : * Access : *
`) `)
}) })
t.Run("share access with --dns and --tls", func(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
state := ultest.Setup(commands)
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
serverCtx, serverCancel := context.WithCancel(ctx)
defer serverCancel()
ctx.Go(func() error {
mux := drpcmux.New()
if err := pb.DRPCRegisterEdgeAuth(mux, &DRPCAuthMockServer{}); err != nil {
return err
}
return drpcserver.New(mux).Serve(serverCtx, listener)
})
authAddr := "insecure://" + listener.Addr().String()
apiKey, err := macaroon.ParseAPIKey(testAPIKey)
require.NoError(t, err)
encAccess := grant.NewEncryptionAccessWithDefaultKey(&storj.Key{})
grantAccess := grant.Access{
SatelliteAddress: "12EayRS2V1kEsWESU9QMRseFhdxYxKicsiFmxrsLZHeLUtdps3S@us1.storj.io:7777",
APIKey: apiKey,
EncAccess: encAccess,
}
access, err := grantAccess.Serialize()
require.NoError(t, err)
expected := `
Sharing access to satellite *
=========== ACCESS RESTRICTIONS ==========================================================
Download : Allowed
Upload : Disallowed
Lists : Allowed
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
========== GATEWAY CREDENTIALS ===========================================================
Access Key ID: accesskeyid
Secret Key : secretkey
Endpoint : endpoint
Public Access: true
=========== DNS INFO =====================================================================
Remember to update the $ORIGIN with your domain name. You may also change the $TTL.
$ORIGIN example.com.
$TTL 3600
test.com IN CNAME link.storjshare.io.
txt-test.com IN TXT storj-root:some/prefix
txt-test.com IN TXT storj-access:accesskeyid
`
state.Succeed(t, "share", "--access", access, "--not-after=none", "--dns", "test.com", "--auth-service", authAddr, "sj://some/prefix").RequireStdoutGlob(t, expected)
expected += "\ntxt-test.com IN TXT storj-tls:true\n"
state.Succeed(t, "share", "--access", access, "--not-after=none", "--dns", "test.com", "--tls", "--auth-service", authAddr, "sj://some/prefix").RequireStdoutGlob(t, expected)
})
}
type DRPCAuthMockServer struct {
pb.DRPCEdgeAuthServer
}
func (g *DRPCAuthMockServer) RegisterAccess(context.Context, *pb.EdgeRegisterAccessRequest) (*pb.EdgeRegisterAccessResponse, error) {
return &pb.EdgeRegisterAccessResponse{
AccessKeyId: "accesskeyid",
SecretKey: "secretkey",
Endpoint: "endpoint",
}, nil
} }