diff --git a/README.debrand b/README.debrand
deleted file mode 100644
index 01c46d2..0000000
--- a/README.debrand
+++ /dev/null
@@ -1,2 +0,0 @@
-Warning: This package was configured for automatic debranding, but the changes
-failed to apply.
diff --git a/SOURCES/sos-bz2011349-replace-dropbox-by-sftp.patch b/SOURCES/sos-bz2011349-replace-dropbox-by-sftp.patch
new file mode 100644
index 0000000..7c26d9b
--- /dev/null
+++ b/SOURCES/sos-bz2011349-replace-dropbox-by-sftp.patch
@@ -0,0 +1,746 @@
+From 5298080d7360202c72b0af2e24994e4bfad72322 Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Fri, 14 May 2021 13:10:04 -0400
+Subject: [PATCH] [Policy] Add SFTP upload support
+
+Adds support for uploading via SFTP. This is done via pexpect calling
+the system's locally available SFTP binary. If either that binary or
+pexpect are unavailable on the local system, we will exit gracefully and
+report the issue. This allows sos to keep python3-pexpect as a
+recommends rather than a hard dependency.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ man/en/sos-report.1              |  14 +++++
+ sos/collector/__init__.py        |   7 ++-
+ sos/policies/distros/__init__.py | 105 +++++++++++++++++++++++++++++--
+ sos/report/__init__.py           |   4 ++
+ 4 files changed, 125 insertions(+), 5 deletions(-)
+
+diff --git a/man/en/sos-report.1 b/man/en/sos-report.1
+index c38753d4a..799defafc 100644
+--- a/man/en/sos-report.1
++++ b/man/en/sos-report.1
+@@ -35,6 +35,7 @@ sosreport \- Collect and package diagnos
+           [--encrypt-pass PASS]\fR
+           [--upload] [--upload-url url] [--upload-user user]\fR
+           [--upload-directory dir] [--upload-pass pass]\fR
++          [--upload-protocol protocol]\fR
+           [--experimental]\fR
+           [-h|--help]\fR
+ 
+@@ -354,6 +355,19 @@ be used provided all other required valu
+ Specify a directory to upload to, if one is not specified by a vendor default location
+ or if your destination server does not allow writes to '/'.
+ .TP
++.B \--upload-protocol PROTO
++Manually specify the protocol to use for uploading to the target \fBupload-url\fR.
++
++Normally this is determined via the upload address, assuming that the protocol is part
++of the address provided, e.g. 'https://example.com'. By using this option, sos will skip
++the protocol check and use the method defined for the specified PROTO.
++
++For RHEL systems, setting this option to \fBsftp\fR will skip the initial attempt to
++upload to the Red Hat Customer Portal, and only attempt an upload to Red Hat's SFTP server,
++which is typically used as a fallback target.
++
++Valid values for PROTO are: 'auto' (default), 'https', 'ftp', 'sftp'.
++.TP
+ .B \--experimental
+ Enable plugins marked as experimental. Experimental plugins may not have
+ been tested for this port or may still be under active development.
+diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
+index 5d1c599ac..1c742cf50 100644
+--- a/sos/collector/__init__.py
++++ b/sos/collector/__init__.py
+@@ -106,6 +106,7 @@ class SoSCollector(SoSComponent):
+         'upload_directory': None,
+         'upload_user': None,
+         'upload_pass': None,
++        'upload_protocol': 'auto'
+     }
+ 
+     def __init__(self, parser, parsed_args, cmdline_args):
+@@ -383,6 +384,9 @@ class SoSCollector(SoSComponent):
+                                  help="Username to authenticate with")
+         collect_grp.add_argument("--upload-pass", default=None,
+                                  help="Password to authenticate with")
++        collect_grp.add_argument("--upload-protocol", default='auto',
++                                 choices=['auto', 'https', 'ftp', 'sftp'],
++                                 help="Manually specify the upload protocol")
+ 
+         # Group the cleaner options together
+         cleaner_grp = parser.add_argument_group(
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index 4268688c6..9fe31513b 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -20,7 +20,7 @@
+ from sos.policies.runtimes.podman import PodmanContainerRuntime
+ from sos.policies.runtimes.docker import DockerContainerRuntime
+ 
+-from sos.utilities import shell_out
++from sos.utilities import shell_out, is_executable
+ 
+ 
+ try:
+@@ -295,7 +295,9 @@ def _determine_upload_type(self):
+             'sftp': self.upload_sftp,
+             'https': self.upload_https
+         }
+-        if '://' not in self.upload_url:
++        if self.commons['cmdlineopts'].upload_protocol in prots.keys():
++            return prots[self.commons['cmdlineopts'].upload_protocol]
++        elif '://' not in self.upload_url:
+             raise Exception("Must provide protocol in upload URL")
+         prot, url = self.upload_url.split('://')
+         if prot not in prots.keys():
+@@ -361,7 +363,7 @@ def get_upload_password(self):
+                 self.upload_password or
+                 self._upload_password)
+ 
+-    def upload_sftp(self):
++    def upload_sftp(self, user=None, password=None):
+         """Attempts to upload the archive to an SFTP location.
+ 
+         Due to the lack of well maintained, secure, and generally widespread
+@@ -371,7 +373,102 @@ def upload_sftp(self):
+         Do not override this method with one that uses python-paramiko, as the
+         upstream sos team will reject any PR that includes that dependency.
+         """
+-        raise NotImplementedError("SFTP support is not yet implemented")
++        # if we somehow don't have sftp available locally, fail early
++        if not is_executable('sftp'):
++            raise Exception('SFTP is not locally supported')
++
++        # soft dependency on python3-pexpect, which we need to use to control
++        # sftp login since as of this writing we don't have a viable solution
++        # via ssh python bindings commonly available among downstreams
++        try:
++            import pexpect
++        except ImportError:
++            raise Exception('SFTP upload requires python3-pexpect, which is '
++                            'not currently installed')
++
++        sftp_connected = False
++
++        if not user:
++            user = self.get_upload_user()
++        if not password:
++            password = self.get_upload_password()
++
++        # need to strip the protocol prefix here
++        sftp_url = self.get_upload_url().replace('sftp://', '')
++        sftp_cmd = "sftp -oStrictHostKeyChecking=no %s@%s" % (user, sftp_url)
++        ret = pexpect.spawn(sftp_cmd, encoding='utf-8')
++
++        sftp_expects = [
++            u'sftp>',
++            u'password:',
++            u'Connection refused',
++            pexpect.TIMEOUT,
++            pexpect.EOF
++        ]
++
++        idx = ret.expect(sftp_expects, timeout=15)
++
++        if idx == 0:
++            sftp_connected = True
++        elif idx == 1:
++            ret.sendline(password)
++            pass_expects = [
++                u'sftp>',
++                u'Permission denied',
++                pexpect.TIMEOUT,
++                pexpect.EOF
++            ]
++            sftp_connected = ret.expect(pass_expects, timeout=10) == 0
++            if not sftp_connected:
++                ret.close()
++                raise Exception("Incorrect username or password for %s"
++                                % self.get_upload_url_string())
++        elif idx == 2:
++            raise Exception("Connection refused by %s. Incorrect port?"
++                            % self.get_upload_url_string())
++        elif idx == 3:
++            raise Exception("Timeout hit trying to connect to %s"
++                            % self.get_upload_url_string())
++        elif idx == 4:
++            raise Exception("Unexpected error trying to connect to sftp: %s"
++                            % ret.before)
++
++        if not sftp_connected:
++            ret.close()
++            raise Exception("Unable to connect via SFTP to %s"
++                            % self.get_upload_url_string())
++
++        put_cmd = 'put %s %s' % (self.upload_archive_name,
++                                 self._get_sftp_upload_name())
++        ret.sendline(put_cmd)
++
++        put_expects = [
++            u'100%',
++            pexpect.TIMEOUT,
++            pexpect.EOF
++        ]
++
++        put_success = ret.expect(put_expects, timeout=180)
++
++        if put_success == 0:
++            ret.sendline('bye')
++            return True
++        elif put_success == 1:
++            raise Exception("Timeout expired while uploading")
++        elif put_success == 2:
++            raise Exception("Unknown error during upload: %s" % ret.before)
++        else:
++            raise Exception("Unexpected response from server: %s" % ret.before)
++
++    def _get_sftp_upload_name(self):
++        """If a specific file name pattern is required by the SFTP server,
++        override this method in the relevant Policy. Otherwise the archive's
++        name on disk will be used
++
++        :returns:       Filename as it will exist on the SFTP server
++        :rtype:         ``str``
++        """
++        return self.upload_archive_name.split('/')[-1]
+ 
+     def _upload_https_streaming(self, archive):
+         """If upload_https() needs to use requests.put(), this method is used
+diff --git a/sos/report/__init__.py b/sos/report/__init__.py
+index df99186db..d43454092 100644
+--- a/sos/report/__init__.py
++++ b/sos/report/__init__.py
+@@ -119,6 +119,7 @@ class SoSReport(SoSComponent):
+         'upload_directory': None,
+         'upload_user': None,
+         'upload_pass': None,
++        'upload_protocol': 'auto',
+         'add_preset': '',
+         'del_preset': ''
+     }
+@@ -300,6 +301,9 @@ class SoSReport(SoSComponent):
+                                 help="Username to authenticate to server with")
+         report_grp.add_argument("--upload-pass", default=None,
+                                 help="Password to authenticate to server with")
++        report_grp.add_argument("--upload-protocol", default='auto',
++                                choices=['auto', 'https', 'ftp', 'sftp'],
++                                help="Manually specify the upload protocol")
+ 
+         # Group to make add/del preset exclusive
+         preset_grp = report_grp.add_mutually_exclusive_group()
+From d5316de87313c3eaf9fe4ce7a5eea3ed8c7d17ce Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Fri, 14 May 2021 13:11:27 -0400
+Subject: [PATCH] [Red Hat] Update policy to use SFTP instead of legacy FTP
+ dropbox
+
+As the FTP dropbox for Red Hat is being decomissioned and replaced with
+an SFTP alternative, update the policy to fallback to the SFTP host and
+remove legacy FTP host references.
+
+The default behavior for --upload remains the same, just targeting a
+different location. If a username, password, and case number are given,
+the first attempt will be to upload to the Red Hat Customer Portal. If
+any are missing, or are invalid, then we will fallback to SFTP. During
+the fallback if a valid username and password are not provided, sos will
+attempt to obtain an anonymous token for the upload before failing out
+entirely.
+
+Closes: #2467
+Resolves: #2552
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/policies/distros/redhat.py | 115 ++++++++++++++++++++++-----------
+ 1 file changed, 76 insertions(+), 39 deletions(-)
+
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index f37519910..241d3f139 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -8,6 +8,7 @@
+ #
+ # See the LICENSE file in the source distribution for further information.
+ 
++import json
+ import os
+ import sys
+ import re
+@@ -20,6 +21,11 @@
+ from sos.policies.package_managers.rpm import RpmPackageManager
+ from sos import _sos as _
+ 
++try:
++    import requests
++    REQUESTS_LOADED = True
++except ImportError:
++    REQUESTS_LOADED = False
+ 
+ OS_RELEASE = "/etc/os-release"
+ RHEL_RELEASE_STR = "Red Hat Enterprise Linux"
+@@ -39,9 +45,8 @@ class RedHatPolicy(LinuxPolicy):
+     _host_sysroot = '/'
+     default_scl_prefix = '/opt/rh'
+     name_pattern = 'friendly'
+-    upload_url = 'dropbox.redhat.com'
+-    upload_user = 'anonymous'
+-    upload_directory = '/incoming'
++    upload_url = None
++    upload_user = None
+     default_container_runtime = 'podman'
+     sos_pkg_name = 'sos'
+     sos_bin_path = '/usr/sbin'
+@@ -196,7 +201,7 @@ def get_tmp_dir(self, opt_tmp_dir):
+ """
+ 
+ RH_API_HOST = "https://access.redhat.com"
+-RH_FTP_HOST = "ftp://dropbox.redhat.com"
++RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
+ 
+ 
+ class RHELPolicy(RedHatPolicy):
+@@ -216,9 +216,7 @@ An archive containing the collected info
+ generated in %(tmpdir)s and may be provided to a %(vendor)s \
+ support representative.
+ """ + disclaimer_text + "%(vendor_text)s\n")
+-    _upload_url = RH_FTP_HOST
+-    _upload_user = 'anonymous'
+-    _upload_directory = '/incoming'
++    _upload_url = RH_SFTP_HOST
+ 
+     def __init__(self, sysroot=None, init=None, probe_runtime=True,
+                  remote_exec=None):
+@@ -260,33 +263,17 @@ def prompt_for_upload_user(self):
+             return
+         if self.case_id and not self.get_upload_user():
+             self.upload_user = input(_(
+-                "Enter your Red Hat Customer Portal username (empty to use "
+-                "public dropbox): ")
++                "Enter your Red Hat Customer Portal username for uploading ["
++                "empty for anonymous SFTP]: ")
+             )
+-            if not self.upload_user:
+-                self.upload_url = RH_FTP_HOST
+-                self.upload_user = self._upload_user
+-
+-    def _upload_user_set(self):
+-        user = self.get_upload_user()
+-        return user and (user != 'anonymous')
+ 
+     def get_upload_url(self):
+         if self.upload_url:
+             return self.upload_url
+-        if self.commons['cmdlineopts'].upload_url:
++        elif self.commons['cmdlineopts'].upload_url:
+             return self.commons['cmdlineopts'].upload_url
+-        # anonymous FTP server should be used as fallback when either:
+-        # - case id is not set, or
+-        # - upload user isn't set AND batch mode prevents to prompt for it
+-        if (not self.case_id) or \
+-           ((not self._upload_user_set()) and
+-               self.commons['cmdlineopts'].batch):
+-            self.upload_user = self._upload_user
+-            if self.upload_directory is None:
+-                self.upload_directory = self._upload_directory
+-            self.upload_password = None
+-            return RH_FTP_HOST
++        elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
++            return RH_SFTP_HOST
+         else:
+             rh_case_api = "/hydra/rest/cases/%s/attachments"
+             return RH_API_HOST + rh_case_api % self.case_id
+@@ -299,27 +286,77 @@ def _get_upload_headers(self):
+     def get_upload_url_string(self):
+         if self.get_upload_url().startswith(RH_API_HOST):
+             return "Red Hat Customer Portal"
+-        return self.upload_url or RH_FTP_HOST
++        elif self.get_upload_url().startswith(RH_SFTP_HOST):
++            return "Red Hat Secure FTP"
++        return self.upload_url
+ 
+-    def get_upload_user(self):
+-        # if this is anything other than dropbox, annonymous won't work
+-        if self.upload_url != RH_FTP_HOST:
+-            return os.getenv('SOSUPLOADUSER', None) or self.upload_user
+-        return self._upload_user
++    def _get_sftp_upload_name(self):
++        """The RH SFTP server will only automatically connect file uploads to
++        cases if the filename _starts_ with the case number
++        """
++        if self.case_id:
++            return "%s_%s" % (self.case_id,
++                              self.upload_archive_name.split('/')[-1])
++        return self.upload_archive_name
++
++    def upload_sftp(self):
++        """Override the base upload_sftp to allow for setting an on-demand
++        generated anonymous login for the RH SFTP server if a username and
++        password are not given
++        """
++        if RH_SFTP_HOST.split('//')[1] not in self.get_upload_url():
++            return super(RHELPolicy, self).upload_sftp()
++
++        if not REQUESTS_LOADED:
++            raise Exception("python3-requests is not installed and is required"
++                            " for obtaining SFTP auth token.")
++        _token = None
++        _user = None
++        # we have a username and password, but we need to reset the password
++        # to be the token returned from the auth endpoint
++        if self.get_upload_user() and self.get_upload_password():
++            url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
++            auth = self.get_upload_https_auth()
++            ret = requests.get(url, auth=auth, timeout=10)
++            if ret.status_code == 200:
++                # credentials are valid
++                _user = self.get_upload_user()
++                _token = json.loads(ret.text)['token']
++            else:
++                print("Unable to retrieve Red Hat auth token using provided "
++                      "credentials. Will try anonymous.")
++        # we either do not have a username or password/token, or both
++        if not _token:
++            aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
++            anon = requests.get(aurl, timeout=10)
++            if anon.status_code == 200:
++                resp = json.loads(anon.text)
++                _user = resp['username']
++                _token = resp['token']
++                print("Using anonymous user %s for upload. Please inform your "
++                      "support engineer." % _user)
++        if _user and _token:
++            return super(RHELPolicy, self).upload_sftp(user=_user,
++                                                       password=_token)
++        raise Exception("Could not retrieve valid or anonymous credentials")
+ 
+     def upload_archive(self, archive):
+         """Override the base upload_archive to provide for automatic failover
+         from RHCP failures to the public RH dropbox
+         """
+         try:
++            if not self.get_upload_user() or not self.get_upload_password():
++                self.upload_url = RH_SFTP_HOST
+             uploaded = super(RHELPolicy, self).upload_archive(archive)
+         except Exception:
+             uploaded = False
+-        if not uploaded and self.upload_url.startswith(RH_API_HOST):
+-            print("Upload to Red Hat Customer Portal failed. Trying %s"
+-                  % RH_FTP_HOST)
+-            self.upload_url = RH_FTP_HOST
+-            uploaded = super(RHELPolicy, self).upload_archive(archive)
++            if not self.upload_url.startswith(RH_API_HOST):
++                raise
++            else:
++                print("Upload to Red Hat Customer Portal failed. Trying %s"
++                      % RH_SFTP_HOST)
++                self.upload_url = RH_SFTP_HOST
++                uploaded = super(RHELPolicy, self).upload_archive(archive)
+         return uploaded
+ 
+     def dist_version(self):
+From 8a7ae6a3ac69a020758f7b0825a872e44714dbed Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Fri, 9 Apr 2021 11:05:47 -0400
+Subject: [PATCH] [ubuntu|Policy] Fix exception when attempting upload
+
+Fixes an issue where an upload attempt on Ubuntu systems would fail with
+a traceback due to the upload archive's name not being available when we
+first check to make sure we have an upload location, since the Ubuntu
+default location requires an archive/file name.
+
+Related to this, stop clobbering `upload_archive` and assign the archive
+name to an `upload_archive_name` variable instead.
+
+Closes: #2472
+Resolves: #2479
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/policies/distros/__init__.py | 9 +++++----
+ sos/policies/distros/ubuntu.py   | 4 +++-
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index 022ba7f4c6..a24a0e5beb 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -149,6 +149,7 @@ def pre_work(self):
+         self.upload_user = cmdline_opts.upload_user
+         self.upload_directory = cmdline_opts.upload_directory
+         self.upload_password = cmdline_opts.upload_pass
++        self.upload_archive_name = ''
+ 
+         if not cmdline_opts.batch and not \
+                 cmdline_opts.quiet:
+@@ -237,7 +238,7 @@ def upload_archive(self, archive):
+         `get_upload_url_string()`
+             Print a more human-friendly string than vendor URLs
+         """
+-        self.upload_archive = archive
++        self.upload_archive_name = archive
+         if not self.upload_url:
+             self.upload_url = self.get_upload_url()
+         if not self.upload_url:
+@@ -384,7 +385,7 @@ def upload_https(self):
+             raise Exception("Unable to upload due to missing python requests "
+                             "library")
+ 
+-        with open(self.upload_archive, 'rb') as arc:
++        with open(self.upload_archive_name, 'rb') as arc:
+             if not self._use_https_streaming:
+                 r = self._upload_https_no_stream(arc)
+             else:
+@@ -467,9 +468,9 @@ def upload_ftp(self, url=None, directory=None, user=None, password=None):
+                             % str(err))
+ 
+         try:
+-            with open(self.upload_archive, 'rb') as _arcfile:
++            with open(self.upload_archive_name, 'rb') as _arcfile:
+                 session.storbinary(
+-                    "STOR %s" % self.upload_archive.split('/')[-1],
++                    "STOR %s" % self.upload_archive_name.split('/')[-1],
+                     _arcfile
+                 )
+             session.quit()
+diff --git a/sos/policies/distros/ubuntu.py b/sos/policies/distros/ubuntu.py
+index 94a4a241b0..308c1e3544 100644
+--- a/sos/policies/distros/ubuntu.py
++++ b/sos/policies/distros/ubuntu.py
+@@ -74,7 +74,9 @@ def get_upload_url_string(self):
+ 
+     def get_upload_url(self):
+         if not self.upload_url or self.upload_url.startswith(self._upload_url):
+-            fname = os.path.basename(self.upload_archive)
++            if not self.upload_archive_name:
++                return self._upload_url
++            fname = os.path.basename(self.upload_archive_name)
+             return self._upload_url + fname
+         super(UbuntuPolicy, self).get_upload_url()
+
+From 2e8b5e2d4f30854cce93d149fc7d24b9d9cfd02c Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Fri, 19 Nov 2021 16:16:07 +0100
+Subject: [PATCH 1/3] [policies] strip path from SFTP upload filename
+
+When case_id is not supplied, we ask SFTP server to store the uploaded
+file under name /var/tmp/<tarball>, which is confusing.
+
+Let remove the path from it also in case_id not supplied.
+
+Related to: #2764
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/redhat.py | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index 3476e21fb..8817fc785 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -269,10 +269,10 @@ def _get_sftp_upload_name(self):
+         """The RH SFTP server will only automatically connect file uploads to
+         cases if the filename _starts_ with the case number
+         """
++        fname = self.upload_archive_name.split('/')[-1]
+         if self.case_id:
+-            return "%s_%s" % (self.case_id,
+-                              self.upload_archive_name.split('/')[-1])
+-        return self.upload_archive_name
++            return "%s_%s" % (self.case_id, fname)
++        return fname
+ 
+     def upload_sftp(self):
+         """Override the base upload_sftp to allow for setting an on-demand
+
+From 61023b29a656dd7afaa4a0643368b0a53f1a3779 Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Fri, 19 Nov 2021 17:31:31 +0100
+Subject: [PATCH 2/3] [redhat] update SFTP API version to v2
+
+Change API version from v1 to v2, which includes:
+- change of URL
+- different URI
+- POST method for token generation instead of GET
+
+Resolves: #2764
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/redhat.py | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index 8817fc785..e4e2b8835 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -175,7 +175,7 @@ def get_tmp_dir(self, opt_tmp_dir):
+ No changes will be made to system configuration.
+ """
+ 
+-RH_API_HOST = "https://access.redhat.com"
++RH_API_HOST = "https://api.access.redhat.com"
+ RH_SFTP_HOST = "sftp://sftp.access.redhat.com"
+ 
+ 
+@@ -287,12 +287,12 @@ def upload_sftp(self):
+                             " for obtaining SFTP auth token.")
+         _token = None
+         _user = None
++        url = RH_API_HOST + '/support/v2/sftp/token'
+         # we have a username and password, but we need to reset the password
+         # to be the token returned from the auth endpoint
+         if self.get_upload_user() and self.get_upload_password():
+-            url = RH_API_HOST + '/hydra/rest/v1/sftp/token'
+             auth = self.get_upload_https_auth()
+-            ret = requests.get(url, auth=auth, timeout=10)
++            ret = requests.post(url, auth=auth, timeout=10)
+             if ret.status_code == 200:
+                 # credentials are valid
+                 _user = self.get_upload_user()
+@@ -302,8 +302,8 @@ def upload_sftp(self):
+                       "credentials. Will try anonymous.")
+         # we either do not have a username or password/token, or both
+         if not _token:
+-            aurl = RH_API_HOST + '/hydra/rest/v1/sftp/token?isAnonymous=true'
+-            anon = requests.get(aurl, timeout=10)
++            adata = {"isAnonymous": True}
++            anon = requests.post(url, data=json.dumps(adata), timeout=10)
+             if anon.status_code == 200:
+                 resp = json.loads(anon.text)
+                 _user = resp['username']
+
+From 267da2156ec61f526dd28e760ff6528408a76c3f Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Mon, 22 Nov 2021 15:22:32 +0100
+Subject: [PATCH 3/3] [policies] Deal 200 return code as success
+
+Return code 200 of POST method request must be dealt as success.
+
+Newly required due to the SFTP API change using POST.
+
+Related to: #2764
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/__init__.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index 0906fa779..6f257fdce 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -488,7 +488,7 @@ class LinuxPolicy(Policy):
+                 r = self._upload_https_no_stream(arc)
+             else:
+                 r = self._upload_https_streaming(arc)
+-            if r.status_code != 201:
++            if r.status_code != 200 and r.status_code != 201:
+                 if r.status_code == 401:
+                     raise Exception(
+                         "Authentication failed: invalid user credentials"
+From 8da1b14246226792c160dd04e5c7c75dd4e8d44b Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Mon, 22 Nov 2021 10:44:09 +0100
+Subject: [PATCH] [collect] fix moved get_upload_url under Policy class
+
+SoSCollector does not further declare get_upload_url method
+as that was moved under Policy class(es).
+
+Resolves: #2766
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/collector/__init__.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py
+index 50183e873..42a7731d6 100644
+--- a/sos/collector/__init__.py
++++ b/sos/collector/__init__.py
+@@ -1219,7 +1219,7 @@ this utility or remote systems that it c
+             msg = 'No sosreports were collected, nothing to archive...'
+             self.exit(msg, 1)
+ 
+-        if self.opts.upload and self.get_upload_url():
++        if self.opts.upload and self.policy.get_upload_url():
+             try:
+                 self.policy.upload_archive(arc_name)
+                 self.ui_log.info("Uploaded archive successfully")
+ 
+From abb2fc65bd14760021c61699ad3113cab3bd4c64 Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Tue, 30 Nov 2021 11:37:02 +0100
+Subject: [PATCH 1/2] [redhat] Fix broken URI to upload to customer portal
+
+Revert back the unwanted change in URI of uploading tarball to the
+Red Hat Customer portal.
+
+Related: #2772
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/redhat.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index e4e2b883..eb442407 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -250,7 +250,7 @@ support representative.
+         elif self.commons['cmdlineopts'].upload_protocol == 'sftp':
+             return RH_SFTP_HOST
+         else:
+-            rh_case_api = "/hydra/rest/cases/%s/attachments"
++            rh_case_api = "/support/v1/cases/%s/attachments"
+             return RH_API_HOST + rh_case_api % self.case_id
+ 
+     def _get_upload_headers(self):
+-- 
+2.31.1
+
+
+From ea4f9e88a412c80a4791396e1bb78ac1e24ece14 Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Tue, 30 Nov 2021 13:00:26 +0100
+Subject: [PATCH 2/2] [policy] Add error message when FTP upload write failure
+
+When (S)FTP upload fails to write the destination file,
+our "expect" code should detect it sooner than after timeout happens
+and write appropriate error message.
+
+Resolves: #2772
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/__init__.py | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index 6f257fdc..7bdc81b8 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -473,7 +473,8 @@ class LinuxPolicy(Policy):
+         put_expects = [
+             u'100%',
+             pexpect.TIMEOUT,
+-            pexpect.EOF
++            pexpect.EOF,
++            u'No such file or directory'
+         ]
+ 
+         put_success = ret.expect(put_expects, timeout=180)
+@@ -485,6 +486,8 @@ class LinuxPolicy(Policy):
+             raise Exception("Timeout expired while uploading")
+         elif put_success == 2:
+             raise Exception("Unknown error during upload: %s" % ret.before)
++        elif put_success == 3:
++            raise Exception("Unable to write archive to destination")
+         else:
+             raise Exception("Unexpected response from server: %s" % ret.before)
+ 
+-- 
+2.31.1
+
diff --git a/SPECS/sos.spec b/SPECS/sos.spec
index 30e6657..95931ee 100644
--- a/SPECS/sos.spec
+++ b/SPECS/sos.spec
@@ -5,7 +5,7 @@
 Summary: A set of tools to gather troubleshooting information from a system
 Name: sos
 Version: 4.1
-Release: 5%{?dist}
+Release: 9%{?dist}
 Group: Applications/System
 Source0: https://github.com/sosreport/sos/archive/%{version}/sos-%{version}.tar.gz
 Source1: sos-audit-%{auditversion}.tgz
@@ -20,6 +20,7 @@ Requires: xz
 Conflicts: vdsm < 4.40
 Obsoletes: sos-collector
 Recommends: python3-pexpect
+Recommends: python3-requests
 Patch1: sos-bz1930181-collect-cleaning-consistency.patch
 Patch2: sos-bz1935603-manpages-see-also.patch
 Patch3: sos-bz1937418-add-cmd-timeout.patch
@@ -42,6 +43,7 @@ Patch19: sos-bz1985986-potential-issues-static-analyse.patch
 Patch20: sos-bz1959598-conversions-and-upgrades.patch
 Patch21: sos-bz1665947-rhui-plugin.patch
 Patch22: sos-bz1985037-cleaner-AD-users-obfuscation.patch
+Patch23: sos-bz2011349-replace-dropbox-by-sftp.patch
 
 
 %description
@@ -75,6 +77,7 @@ support technicians and developers.
 %patch20 -p1
 %patch21 -p1
 %patch22 -p1
+%patch23 -p1
 
 %build
 %py3_build
@@ -141,6 +144,14 @@ of the system. Currently storage and filesystem commands are audited.
 %ghost /etc/audit/rules.d/40-sos-storage.rules
 
 %changelog
+* Tue Nov 30 2021 Pavel Moravec <pmoravec@redhat.com> = 4.1-9
+- [redhat] Fix broken URI to upload to customer portal
+  Resolves: bz2011349
+
+* Mon Nov 22 2021 Pavel Moravec <pmoravec@redhat.com> = 4.1-8
+- [Red Hat] Update policy to use SFTP, update RHST API to v2
+  Resolves: bz2011349
+
 * Wed Aug 11 2021 Pavel Moravec <pmoravec@redhat.com> = 4.1-5
 - [report,collect] unify --map-file arguments
   Resolves: bz1923938