Skip Navigation
Show nav
Heroku Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
View categories

Categories

  • Heroku Architecture
    • Dynos (app containers)
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Command Line
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery
    • Continuous Integration
  • Language Support
    • Node.js
    • Ruby
      • Working with Bundler
      • Rails Support
    • Python
      • Background Jobs in Python
      • Working with Django
    • Java
      • Working with Maven
      • Java Database Operations
      • Working with the Play Framework
      • Working with Spring Boot
      • Java Advanced Topics
    • PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
    • Heroku Redis
    • Apache Kafka on Heroku
    • Other Data Stores
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
    • Compliance
  • Heroku Enterprise
    • Private Spaces
      • Infrastructure Networking
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
    • Single Sign-on (SSO)
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Add-ons
  • All Add-ons
  • SFTP To Go
SFTP To Go

This add-on is operated by Crazy Ant Labs

Managed SFTP/FTPS cloud storage as a service with Amazon S3 and HTTP file access

SFTP To Go

Last updated February 02, 2021

Table of Contents

  • Provisioning the add-on
  • Local setup
  • Configuring DNS for custom subdomain
  • Using with Linux/Mac command line interface
  • Using with data integration platforms
  • Using with graphical client tools
  • Using with Ruby
  • Using with Node
  • Using with Go
  • Using with other languages
  • Amazon S3 access
  • Using with the AWS CLI
  • Dashboard
  • Credentials and permissions
  • Webhooks
  • Inbound network rules
  • SFTP Connection via IP addresses
  • Migrating between plans
  • Removing the add-on
  • Known issues and remedies
  • Security
  • Support

SFTP To Go allows you to add fully-managed cloud SFTP storage to your Heroku applications. SFTP is a standard network protocol used for secure file access, transfer, and management.

Having a managed SFTP service enables you to participate in secure data exchange with partners and customers in a standard protocol, without purchasing and running your own SFTP servers and storage. SFTP To Go is based on AWS offerings, making it highly scalable and highly available with multi-AZ architecture.

With SFTP To Go, you can also use FTPS and S3 to access your files and share them with others.

Provisioning the add-on

SFTP To Go can be attached to a Heroku application via the CLI:

$ heroku addons:create sftptogo
-----> Adding sftptogo to sharp-mountain-4005... done, v18 (free)

A list of all plans available can be found here.

After you provision SFTP To Go, the SFTPTOGO_URL config var is available in your app’s configuration. It contains the URL to your provisioned SFTP service, including the root username, password, and host. You can confirm this via the heroku config:get command:

$ heroku config:get SFTPTOGO_URL
sftp://user:pass@server.sftptogo.com

The root user has read/write access to the entirety of your SFTP service.

Once you provision the SFTP To Go add-on, there is nothing to set up. You and your application can immediately access your SFTP service.

Local setup

Environment setup

After you provision the add-on, you must locally replicate its configuration variables to ensure that your development environment can operate against the service.

Use the Heroku Local command-line tool to configure, run and manage process types specified in your app’s Procfile. Heroku Local reads configuration variables from a .env file. To view all of your app’s config vars, type heroku config. Use the following command for each value that you want to add to your .env file:

$ heroku config:get SFTPTOGO_URL -s  >> .env

Credentials and other sensitive configuration values should not be committed to source-control. In Git exclude the .env file with: echo .env >> .gitignore.

For more information, see the Heroku Local article.

Configuring DNS for custom subdomain

After provisioning the add-on, you can add a DNS record to use your subdomain instead of SFTP To Go’s host name. To do so, copy your add-on host name from the dashboard and add a CNAME record to point to the host name. For example:

Record Name Target
CNAME sftp yellow-rectangle-14793.sftptogo.com.

Consult your DNS provider’s documentation for specific instructions on creating CNAME records.

Using with Linux/Mac command line interface

Start an interactive SFTP session using the following command:

$ sftp user@host

You will then be prompted to enter your password.

Some of the simple commands you can use with SFTP:

Command Description
cd path change the remote working directory to path.
mkdir path create the remote directory path.
rmdir path removes the remote directory path if empty.
ls [path] display remote path listing of path, or of current remote directory if path is excluded.
get [-r] remote-path [local-path] retrieve remote-path and store on local machine (at either local-path, or in the current local working directory with the same filename as on the remote server). If the -r flag is specified, then remote-path will be copied recursively.
put [-r] local-path [remote-path] upload local-path and store on remote server. If remote-path is omitted, the file is given the same filename as on your local machine, and is stored in the current remote working directory. If the -r flag is specified, then local-path will be copied recursively.
rm remote-path removes the remote file(s) corresponding with remote-path.
bye quit sftp session.

Using with data integration platforms

Modern data integration platforms allow you to easily read data files from SFTP or write files into SFTP, ultimately supporting easy integration of data from a variety of sources for further analysis and BI processes.

For example, to connect from Xplenty, a data integration add-on found in Heroku, to SFTP To Go, create an SFTP connection through Xplenty. Go to SFTP To Go’s dashboard, copy the host, user and password, and paste them in Xplenty’s new SFTP Connection page, according to the steps described here.

Using with graphical client tools

There are many available SFTP and FTPS GUI clients, such as Cyberduck, WinSCP, FileZilla, Transmit and CloudBerry. For example, to use Cyberduck with SFTP To Go, copy the value of your SFTPTOGO_URL config var to CyberDuck and click Quick Connect.

You should always use port 21 with FTPS and port 22 with SFTP.

Using with Ruby

Ruby applications need to add the following entry into their Gemfile specifying the Net-SFTP client library:

gem 'net-sftp'

Update application dependencies with bundler:

$ bundle install

To download a file:

require 'net/sftp'
require 'uri'

sftptogo_url = ENV['SFTPTOGO_URL']

uri = URI.parse(sftptogo_url)

Net::SFTP.start(uri.host, uri.user, :password => uri.password) do |sftp|
  # download a file or directory from the remote host
  sftp.download!("/path/to/remote", "/path/to/local")
end

Using with Node

Node applications need to add the following entry into their package.json, specifying the dependency on the ssh2-sftp-client library:

{
  "name": "your-app-name",
  "dependencies": {
    "ssh2-sftp-client": "^5.2.2" ...
    }
}

You can also install it locally:

$ npm install ssh2-sftp-client

To list files:

let Client = require('ssh2-sftp-client');
let sftp = new Client();

sftp.connect({
  host: 'myserver.sftptogo.com',
  port: '22',
  username: 'username',
  password: '******'
}).then(() => {
  return sftp.list('/home');
}).then(data => {
  console.log(data, 'the data info');
}).catch(err => {
  console.log(err, 'catch error');
});

See more examples here.

Using with Go

In your app’s go.mod file, add dependencies to the following:

require (
    golang.org/x/crypto/ssh
    golang.org/x/crypto/ssh/agent
    github.com/pkg/sftp
)

The following code shows how to connect to SFTP To Go (using the environment variable), list files, upload and download a file.

package main

import (
    "fmt"
    "io"
    "os"
    "net"
    "net/url"
    "bufio"
    "strings"
    "path/filepath"

    "golang.org/x/crypto/ssh"
    "golang.org/x/crypto/ssh/agent"
    "github.com/pkg/sftp"
)

func main() {
    // Get SFTP To Go URL from environment
    rawurl := os.Getenv("SFTPTOGO_URL")

    parsedUrl, err := url.Parse(rawurl)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Failed to parse SFTP To Go URL: %s\n", err)
        os.Exit(1)
    }

    // Get user name and pass
    user := parsedUrl.User.Username()
    pass, _ := parsedUrl.User.Password()

    // Parse Host and Port
    host := parsedUrl.Host
    // Default SFTP port
    port := 22

    hostKey := getHostKey(host)

    fmt.Fprintf(os.Stdout, "Connecting to %s ...\n", host)

    var auths []ssh.AuthMethod

    // Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
    // for communication with other processes.
    if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
        auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
    }

    // Use password authentication if provided
    if pass != "" {
        auths = append(auths, ssh.Password(pass))
    }

    // Initialize client configuration
    config := ssh.ClientConfig{
        User: user,
        Auth: auths,
        // Uncomment to ignore host key check
        //HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        HostKeyCallback: ssh.FixedHostKey(hostKey),
    }

    addr := fmt.Sprintf("%s:%d", host, port)

    // Connect to server
    conn, err := ssh.Dial("tcp", addr, &config)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Failed to connecto to [%s]: %v\n", addr, err)
        os.Exit(1)
    }

    defer conn.Close()

    // Create new SFTP client
    sc, err := sftp.NewClient(conn)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to start SFTP subsystem: %v\n", err)
        os.Exit(1)
    }
    defer sc.Close()

    //*
    //* List working directory files
    //*
    listFiles(*sc, ".")
    fmt.Fprintf(os.Stdout, "\n")

    //*
    //* Upload local file to remote file
    //*
    uploadFile(*sc, "./local.txt", "./remote.txt")
    fmt.Fprintf(os.Stdout, "\n")

    //*
    //* Download remote file to local file
    //*
    downloadFile(*sc, "./remote.txt", "./download.txt")
    fmt.Fprintf(os.Stdout, "\n")
}

func listFiles(sc sftp.Client, remoteDir string) (err error) {
    fmt.Fprintf(os.Stdout, "Listing [%s] ...\n\n", remoteDir)

    files, err := sc.ReadDir(remoteDir)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to list remote dir: %v\n", err)
        return
    }

    for _, f := range files {
        var name, modTime, size string

        name = f.Name()
        modTime = f.ModTime().Format("2006-01-02 15:04:05")
        size = fmt.Sprintf("%12d", f.Size())

        if f.IsDir() {
            name = name + "/"
            modTime = ""
            size = "PRE"
        }
        // Output each file name and size in bytes
        fmt.Fprintf(os.Stdout, "%19s %12s %s\n", modTime, size, name)
    }

    return
}


// Upload file to sftp server
func uploadFile(sc sftp.Client, localFile, remoteFile string) (err error) {
    fmt.Fprintf(os.Stdout, "Uploading [%s] to [%s] ...\n", localFile, remoteFile)

    srcFile, err := os.Open(localFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to open local file: %v\n", err)
        return
    }
    defer srcFile.Close()

    // Make remote directories recursion
    parent := filepath.Dir(remoteFile)
    path := string(filepath.Separator)
    dirs := strings.Split(parent, path)
    for _, dir := range dirs {
        path = filepath.Join(path, dir)
        sc.Mkdir(path)
    }

    // Note: SFTP To Go doesn't support O_RDWR mode
    dstFile, err := sc.OpenFile(remoteFile, (os.O_WRONLY|os.O_CREATE|os.O_TRUNC))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to open remote file: %v\n", err)
        return
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to upload local file: %v\n", err)
        os.Exit(1)
    }
    fmt.Fprintf(os.Stdout, "%d bytes copied\n", bytes)

    return
}

// Download file from sftp server
func downloadFile(sc sftp.Client, remoteFile, localFile string) (err error) {

    fmt.Fprintf(os.Stdout, "Downloading [%s] to [%s] ...\n", remoteFile, localFile)
    // Note: SFTP To Go doesn't support O_RDWR mode
    srcFile, err := sc.OpenFile(remoteFile, (os.O_RDONLY))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to open remote file: %v\n", err)
        return
    }
    defer srcFile.Close()

    dstFile, err := os.Create(localFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to open local file: %v\n", err)
        return
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to download remote file: %v\n", err)
        os.Exit(1)
    }
    fmt.Fprintf(os.Stdout, "%d bytes copied\n", bytes)

    return
}

// Get host key from local known hosts
func getHostKey(host string) ssh.PublicKey {
    // parse OpenSSH known_hosts file
    // ssh or use ssh-keyscan to get initial key
    file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to read known_hosts file: %v\n", err)
        os.Exit(1)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    var hostKey ssh.PublicKey
    for scanner.Scan() {
        fields := strings.Split(scanner.Text(), " ")
        if len(fields) != 3 {
            continue
        }
        if strings.Contains(fields[0], host) {
            var err error
            hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
            if err != nil {
                fmt.Fprintf(os.Stderr, "Error parsing %q: %v\n", fields[2], err)
                os.Exit(1)
            }
            break
        }
    }

    if hostKey == nil {
        fmt.Fprintf(os.Stderr, "No hostkey found for %s", host)
        os.Exit(1)
    }

    return hostKey
}

Using with other languages

To use SFTP To Go with other languages, use language-specific SFTP SDKs (e.g., Python, Java)

Amazon S3 access

S3 access is only available for Gold plan users and above

In addition to the SFTP protocol, SFTP To Go allows you to use S3 APIs to access the same data. This allows you to share data with some users or applications using SFTP and with others, using S3, out of the box. When the add-on is provisioned, the following environment variables are added to your app to allow programmatic access using S3 APIs:

  • SFTPTOGO_AWS_ACCESS_KEY_ID - IAM access key
  • SFTPTOGO_AWS_SECRET_ACCESS_KEY - IAM secret
  • SFTPTOGO_AWS_BUCKET - bucket name
  • SFTPTOGO_AWS_REGION - AWS region

You can also use the values of these environment variables to access your data using Amazon S3 CLI and GUI clients.

Note that the values (especially access key and secret) may change, so please refer to the environment variables only

We recommend to rotate S3 access keys every 90 days.

Using with the AWS CLI

If you have Amazon S3 access, you can also use the AWS CLI to interact with your files.

After you install the AWS CLI, you can configure it with the SFTPTOGO_AWS_* settings in your app or through your dashboard:

$ aws configure
AWS Access Key ID [None]: <$SFTPTOGO_AWS_ACCESS_KEY_ID>
AWS Secret Access Key [None]: <$SFTPTOGO_AWS_SECRET_ACCESS_KEY>
Default region name [None]: <$SFTPTOGO_AWS_REGION>

The following table lists the most popular commands used when activating the AWS CLI

Operation Command
Upload file aws s3 cp image.png s3://$SFTPTOGO_AWS_BUCKET/home/
Download file aws s3 cp s3://$SFTPTOGO_AWS_BUCKET/home/image.png image2.png
List files aws s3 ls s3://$SFTPTOGO_AWS_BUCKET/home/
Delete file aws s3 rm s3://$SFTPTOGO_AWS_BUCKET/home/image.png

Dashboard

Use The SFTP To Go dashboard to:

  1. Access your SFTP server information (host, user name, and password),
  2. Rotate your password. This generates a new password and changes the URI in the SFTPTOGO_URL config var.

We recommend rotating passwords every 90 days. The credential list shows the password age for each credential.

  1. Get storage and bandwidth metrics to monitor your add-on usage.
  2. Add/remove/modify/activate/deactivate credentials (see below).

You can access the dashboard via the CLI:

$ heroku addons:open sftptogo
Opening sftptogo for sharp-mountain-4005

or by visiting the Heroku Dashboard and selecting the application in question. Select SFTP To Go from the Add-ons menu.

Credentials and permissions

To create more credentials to use within the same add-on instance using the UI:

  1. Click Add credential.
  2. Choose a nickname for the credential (optional). This only shows up in the UI.
  3. This is an optional step. Select a home directory for the credential. By default, each credential only has access to its own home directory. You can change the credential’s home directory to have multiple credentials access the same directory.
  4. Select the level of permissions accessible for the new user. By default the user has read-only access to their home directory:
Permissions
Read-only List files and directories
Get files
Write-only List files and directories
Create directories
Remove empty directories
Put files (no overwrite)
Read-Write List files and directories
Create directories
Remove directories
Put files
Get files
None Disabled login
Full Access
(root)
List files and directories
Create directories
Remove directories
Put files
Get files
Access all directories (i.e. root dir is the account’s root directory)
  1. Click Add credential. The user will be assigned a random username and password.
  2. You may deactivate a user to revoke their access while maintaining the same credential information.

To import public ssh keys to use with the user name instead of password:

  1. Click Import SSH key.
  2. Generate a new key pair or copy an existing public key (usually ends with .pub). You can generate a new key pair using ssh-keygen -t rsa on Linux/Mac or using PuTTYgen on Windows. Make sure you generate a new RSA key.
  3. Paste the public key. Make sure it begins with ssh-rsa.
  4. Click Import SSH key

Webhooks

Webhooks enable you to receive notifications whenever particular events occur within your SFTP To Go add-on. You can subscribe to notifications for the following events:

  • File / directory creation
  • File / directory deletion

Webhook notifications are sent as HTTPS POST requests to a URL of your choosing. To integrate with webhooks, you need to implement a server endpoint that receives and handles these requests.

Please note that our webhooks don’t work with self-signed certs. If a webhook detects a self-signed cert, it will result in an error and no request will be sent.

To subscribe to webhooks, go to the webhooks interface found in the dashboard; Webhooks. Then click the Add webhook button.

In the dialog that opens, fill out the following:

  • Nickname (optional) - a descriptive name for your webhook.
  • Endpoint URL - HTTPS URL of your server endpoint that will receive all webhook notifications.
  • Authorization Header (optional) - a custom Authorization header that will be included with all webhook notifications.
  • Topics - select the types of notifications you want to be informed about. You must include at least one topic.

Finish by clicking Add webhook.

Securing Webhooks

When a webhook is created, a signing secret is generated and displayed once.

Each request is signed by SFTP To Go in the X-Hub-Signature header. If you’d like to verify the authenticity of our request, copy the signing secret and use it to verify the webhook signature. You can rotate the signing secret at any time to be assigned a new one.

You may also use an authorization header to verify that the request did, indeed, come from SFTP To Go. When properly set, the authorization header is passed through in the Authorization header in the request. It should be validated using the authorization mechanism of your choice on your server.

Managing Webhooks

After creating a webhook, you may do the following:

  • Pause/Resume - temporarily pause webhook notifications or resume them.
  • Rotate secret - see Securing Webhooks
  • Ping webhook - manually send a test event to your endpoint
  • View deliveries - View a log of the notifications that SFTP To Go has enqueued for delivery. Each notification has a status (one of Succeeded, Failed, ‘Pending’), Created timestamp, ID, Topic (one of job.execution.failed, job.execution.started, job.execution.succeeded, webhook.ping) and Duration. You may also view the Request payload, Response code and Response body.

Receiving Webhooks

When a webhook event that you’ve subscribed to occurs, SFTP To Go sends a POST request to your server endpoint consisting of events’ details.

You can verify the authenticity of these requests through any of the following ways:

  • The request’s Authorization header matches the value you provided when subscribing to notifications.
  • The request’s X-Hub-Signature header contains the HMAC SHA256 signature of the request body (signed with the secret value provided when subscribing).

A resulting webhook notification request resembles the following:

POST https://webhook.site/394f2074-e56f-4110-7bf7-ca14a1f48b7c
Authorization: Bearer 01234567-89ab-cdef-0123-456789abcdef
X-Hub-Signature: cLcN5U5x+jHEkANnVaaRwBw7yE4uv4pXdjcY9Cajc7M=
{
  "Id": "2e579289-4c4a-4085-9b43-74020865cdda",
  "Topic": "webhook.ping",
  "UpdatedAt": 1590911947688,
  "CreatedAt": 1590911947688,
  "Actor": {
    "Type": "IAM",
    "Id": "AIDAIKEQXZMPCW5OVTUWU"
  },
  "Resource": "webhook",
  "PreviousData": null,
  "Data": {
    "Topics": [
      "file.deleted"
    ],
    "State": "enabled",
    "Alias": "Test Webhook",
    "CreatedAt": 1590501031122,
    "Id": "6b1c6f0c-52c1-47a6-9344-57a4579ced68",
    "OrganizationId": "d57060b1-23fe-2d59-afd0-7f56d9e1fc55",
    "UpdatedAt": 1590911941179,
    "Url": "https://webhook.site/394f2074-e56f-4110-7bf7-ca14a1f48b7c"
  },
  "Metadata": {
    "Webhook": {
      "Id": "6b1c6f0c-52c1-47a6-9344-57a4579ced68"
    },
    "Delivery": {
      "Id": "c7ce90e6-5708-43b1-a052-13991f45c771"
    },
    "Attempt": {
      "Id": "2e241efe-d22f-4fe7-aab9-adcde63fca6d"
    },
    "Event": {
      "Id": "2e579289-4c4a-4085-9b43-74020865cdda",
      "Topic": "webhook.ping"
    }
  }
}

You should always respond with a 200-level status code to indicate that you received the notification. SFTP To Go ignores the body of your response, so a 204 status with an empty body is ideal:

204 No Content

If you do not return a 200-level status code, SFTP To Go records the failure. The failure can be viewed in the deliveries log.

The Actor key contains information on the user who performed the action. If the action was performed by an SFTP user, the Type would be User and the Id would be the username. If the action was performed by an IAM user (via S3 APIs), the Type would be IAM and the Id would be the IAM user ID.

file.created Event Format

{
  "Id": "36cd78bc-0662-43b1-a7aa-0cde4876ab2a",
  "Topic": "file.created",
  "CreatedAt": 1591106805970,
  "UpdatedAt": 1591106805970,
  "Actor": {
    "Type": "User",
    "Id": "4ddb9e1265b8edb7685b4e1a5d129f"
  },
  "Resource": "File",
  "PreviousData": null,
  "Data": {
    "Path": "dir/file1.txt",
    "Size": 357464
  },
  "Metadata": {
    "Organization": {
      "Id": "d57060b1-23fe-2d59-afd0-7f56d9e1fc55"
    },
    "Webhook": {
      "Id": "7f3c5b1d-5df4-409f-bbbd-3ac6a72d8b5a"
    },
    "Delivery": {
      "Id": "0a4ed750-fbb8-4718-8a6c-86c35c9b6348"
    },
    "Attempt": {
      "Id": "2cb02803-b3a4-4ccd-bd19-ad769c51b291"
    },
    "Event": {
      "Id": "36cd78bc-0662-43b1-a7aa-0cde4876ab2a",
      "Topic": "file.created"
    }
  }
}

When a Data.Path value ends with /, this indicates that a directory has been created. In all other instances, a file had been created.

file.deleted Event Format

{
  "Id": "f63ad40e-711d-482b-979a-dca97281d8b7",
  "Topic": "file.deleted",
  "CreatedAt": 1591106814353,
  "UpdatedAt": 1591106814353,
  "Actor": {
    "Type": "IAM",
    "Id": "AIDAIKEQXZMPCW5OVTUWU"
  },
  "Resource": "File",
  "PreviousData": null,
  "Data": {
    "Path": "dir/file1.txt",
    "Size": null
  },
  "Metadata": {
    "Organization": {
      "Id": "d57060b1-23fe-2d59-afd0-7f56d9e1fc55"
    },
    "Webhook": {
      "Id": "7f3c5b1d-5df4-409f-bbbd-3ac6a72d8b5a"
    },
    "Delivery": {
      "Id": "aecd305e-f080-4c87-b40b-6b3d5d0d17db"
    },
    "Attempt": {
      "Id": "7dc9f0dc-06ad-4585-a9fd-5ac36d73f817"
    },
    "Event": {
      "Id": "f63ad40e-711d-482b-979a-dca97281d8b7",
      "Topic": "file.deleted"
    }
  }
}

When a Data.Path value ends with /, this indicates that a directory has been created. In all other instances, a file had been created.

webhook.ping Event Format

{
  "Id": "2e579289-4c4a-4085-9b43-74020865cdda",
  "Topic": "webhook.ping",
  "CreatedAt": 1590911947688,
  "UpdatedAt": 1590911947688,
  "Resource": "webhook",
  "PreviousData": null,
  "Data": {
    "Topics": [
      "job.execution.succeeded",
      "job.execution.failed",
      "job.execution.started"
    ],
    "State": "paused",
    "Alias": "Test Webhook",
    "CreatedAt": 1590501031122,
    "Id": "6b1c6f0c-52c1-47a6-9344-57a4579ced68",
    "OrganizationId": "d57060b1-23fe-2d59-afd0-7f56d9e1fc55",
    "UpdatedAt": 1590911941179,
    "Url": "https://webhook.site/394f2074-e56f-4110-7bf7-ca14a1f48b7c"
  },
  "Metadata": {
    "Webhook": {
      "Id": "6b1c6f0c-52c1-47a6-9344-57a4579ced68"
    },
    "Delivery": {
      "Id": "c7ce90e6-5708-43b1-a052-13991f45c771"
    },
    "Attempt": {
      "Id": "2e241efe-d22f-4fe7-aab9-adcde63fca6d"
    },
    "Event": {
      "Id": "2e579289-4c4a-4085-9b43-74020865cdda",
      "Topic": "webhook.ping"
    }
  }
}

Inbound network rules

Inbound network rules control the incoming traffic to your server by defining a safe list of IP addresses or IP address ranges using CIDR notation. By default, access from any IP to use your credentials with any protocol (SFTP or FTPS) is allowed. You can create up to 10 rules per add-on.

Non-enterprise plans use shared resources. For these plans, the inbound network rules define which IP addresses and address ranges can be used with the credentials defined for your add-on. Inbound network rules do not apply to S3 access.

 

Inbound network rules are available for Gold plan users and above.

To create a new inbound rule:

  1. Click Settings.
  2. Click Add new inbound rule.
  3. Select the allowed protocols.
  4. Fill out the source IP address or CIDR for IP address range.
  5. Add an optional description.
  6. Click Add inbound rule.

You can also edit, disable, enable and delete inbound network rules.

SFTP Connection via IP addresses

Some legacy systems require an IP address to connect to an SFTP server. We use static IPs with our services so you can continue and use the same IP addresses over time. To find your host’s underlying IP addresses, perform an nslookup on your the hostname.

Migrating between plans

You can either use the UI to upgrade or downgrade SFTP To Go, or use the heroku addons:upgrade command to migrate to a new plan.

$ heroku addons:upgrade sftptogo:newplan
-----> Upgrading sftptogo:[[newplan]] to sharp-mountain-4005... done, v18 ($50/mo)
       Your plan has been updated to: sftptogo:newplan

Removing the add-on

You can remove SFTP To Go via the CLI:

This will destroy all associated data and cannot be undone!

$ heroku addons:destroy sftptogo
-----> Removing sftptogo from sharp-mountain-4005... done, v20 (free)

Before removing SFTP To Go, you can download your data using your favorite sftp client.

Known issues and remedies

Troubleshooting slow connection time

If you see that opening a connection to SFTP To Go takes a long time, or come across connection timeouts in your client, follow these steps to troubleshoot:

  1. Try to connect to SFTP To Go from a different network - there may be issues in your local network that are causing these connectivity issues. If it works well on the new network,it is recommended that you resolve the issues found within your network.

  2. Try to connect to SFTP To Go from a different computer - it may be your computer and services (e.g. anti-virus apps) on it that are causing these connectivity issues. If it works well on the new computer, it is recommended that you resolve issues found within your computer.

  3. If after running these two tests, you still experience slow connection time or connection timeouts, use the following command pattern on a Mac console to get a client side log with timestamps. Replace URL with your add-on URL (copied from the add-on dashboard):

sftp -v URL 2>&1 | while read line; do echo "`date -u +"%Y-%m-%dT%H:%M:%SZ"` $line"; done

For example:

sftp -v sftp://a9fe70b82zbb94f7160dfcBfcd7@yellow-rectangle-14793.sftptogo.com 2>&1 | while read line; do echo "`date -u +"%Y-%m-%dT%H:%M:%SZ"` $line"; done

Paste your password when prompted to do it, and hit enter/return.

Wait until you see the text Connected to ... .sftptogo.com. and then type quit and hit enter/return.

Copy the client log and send it to us in addition to your computer’s time zone, so we can investigate what happened on the servers at the same time.

How to handle FileZilla connection timeout to SFTP To Go

If you use FileZilla to connect to SFTP To Go and keep getting error messages such as Connection timed out after 20 seconds of inactivity, follow these steps to change client side configuration:

  • On Windows, click Edit >> Settings
  • On Mac, click FileZilla >> Settings
  • Under [Connection], increase the value for timeout in seconds to 600 (default is 20 seconds).
  • Click [OK] to save you changes and connect again.

Filezilla settings screenshot

Uploaded files are corrupted or empty

Try to reproduce the issue while running Wireshark to capture network packets and send us Wireshark’s output along with the paths to your corrupted files and the name and version of your SFTP client.

There are known issues that are the result of using clients with Perl modules. If you can’t avoid using Perl modules please make sure to take the following steps:

  1. Use the option backend => 'Net_SSH2'. Add this to %attr and remove QueueDepth=1.
  2. Make sure that you have access to the CPAN module Net::SFTP::Foreign::Backend::Net_SSH2, located on the metacpan website.
  3. Use plink or run "sftp" within your Perl script using batch mode. Batch mode works well in this case because the command itself is interactive.

When using the S3 access keys, I am unable to connect

Make sure to use the bucket (found in SFTPTOGO_AWS_BUCKET) when you connect to S3; otherwise, your client will try to list all buckets but since it doesn’t have permissions to do so, it will fail.

Supported and unsupported file operations in SFTP

All common commands to create, read, update and delete files and directories are supported.

Files are stored as objects on Amazon S3 and directories are managed as folder objects in S3. Directory rename operations, file append operations, changing ownership, permissions and file timestamps, and use of symbolic and hard links are not supported

Known limitations with FTPS

  1. Only Explicit mode is supported. Implicit mode is not supported.
  2. Only Passive mode is supported.
  3. Only STREAM mode is supported.
  4. Only Image/Binary mode is supported.
  5. TLS - PROT C (unprotected) TLS for the data connection is the default.

Security

  • The data you store on SFTP To Go is encrypted in transit and at rest on the server side.
  • Our physical infrastructure is hosted on Amazon Web Services’ (AWS) data centers.
  • AWS Security Groups (virtual firewalls) are utilized to restrict access to our network from external networks and between systems internally.
  • Backend access to the OS level and AWS console is limited to Crazy Ant Labs staff and requires key authentication and multi factor authentication.

Support

All SFTP To Go support and runtime issues should be submitted via one of the Heroku Support channels. Any non-support related issues or product feedback is welcome at hello@crazyantlabs.com.

Keep reading

  • All Add-ons

Feedback

Log in to submit feedback.

Ziggeo Shared Workforce

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Podcasts
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing

Subscribe to our monthly newsletter

Your email address:

  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Heroku Podcasts
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Facebook
  • Instagram
  • Github
  • LinkedIn
  • YouTube
Heroku is acompany

 © Salesforce.com

  • heroku.com
  • Terms of Service
  • Privacy
  • Cookies