Skip to content

Add file getting, sending to cli.py#29

Open
SteveMicroNova wants to merge 1 commit into
mainfrom
TransferFiles
Open

Add file getting, sending to cli.py#29
SteveMicroNova wants to merge 1 commit into
mainfrom
TransferFiles

Conversation

@SteveMicroNova
Copy link
Copy Markdown
Contributor

Decided to close #11 as a way of getting my feet wet in the support tunnel side of things
This implementation requires you to use a google auth token for every interaction, so transferring many files that are in different folders or directories will require multiple auth token verifications (inputting codes sent to email or phone) and may run the risk of exhausting your auth token limit for a few hours at a time.

Comment thread admin/cli.py
import logging

from os import getenv
from os import getenv, path, makedirs, walk
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed we imported specific things from os instead of just importing the whole thing, I assumed that was for a reason and kept up the practice

Comment thread admin/cli.py
Comment on lines +72 to +109
def connect_tunnel(c, tunnel_id):
"""Connects to a live support tunnel using the tunnel_id, and returns a connection to the device"""
# Care should be exercised here; we're taking data from a remote source and using it to
# run shell commands. Validate every last bit of data.
t = get_tunnel(tunnel_id)
assert TunnelState(t['state']) == TunnelState.running, "Device has not yet connected"
dip = device_ip(IPv4Network(t['network'])).ip
assert t['support_user'].isalnum()
assert t['support_user'].isascii()
support_user = t['support_user']

# set up local
c.run("gcloud compute config-ssh", hide="both")
user_from_oslogin = c.run("gcloud compute os-login describe-profile --format=json", hide="both")
ts_user = json.loads(user_from_oslogin.stdout)['posixAccounts'][0]['username']

# set up connection to bastion
ts = Connection(
host = str(get_ts_instance_public_ip(tunnel_id)),
user = ts_user,
connect_kwargs={"auth_timeout": 120}
)

# grab the ssh private key on the tunnel server
ssh_privkey = ts.run(f"sudo cat {SSH_KEYFILE_PATH}", hide="both")
assert ssh_privkey

# set up connection to destination device
device = Connection(
host=str(dip),
user=support_user,
gateway=ts,
connect_kwargs = {
"pkey": Ed25519Key.from_private_key(io.StringIO(ssh_privkey.stdout)),
}
)

return device
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved a majority of the fab connect task to its own function, as that was being reused by my new tasks to access the remote device
This causes a new auth token verification to be required for every task that uses this function, which could exhaust the 2FA limit that google has before they cut you off for a few hours. It'd be nicer to find a secure way to store the session data to be reused instead but I'll need help with that if we're interested in going down that path

Comment thread admin/cli.py
)
def send_file(remote_device, local_file, remote_dir):
filename = path.basename(local_file)
remote_path = path.join(remote_dir, filename).replace("./", "") # path.join adds an unneccessary ./ when combining things with their own directory, making the prints down the line look odd
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had an experience where Uploading /home/steve/folder/file5.html to /etc/folder/./file5.html... was being printed, the replace prevents that extra ./ in cases where the path.join is joining just the filename to the directory rather than joining a subdirectory to the directory

Comment thread admin/cli.py
Comment on lines +288 to +295
# Support user lacks permissions to send file to just any directory, scrape the filename and send to an intermediary and then sudo mv it to the proper location
print(f"Uploading {local_file} to {remote_path}...")
temp_remote = f"/tmp/{filename}"
remote_device.put(local=local_file, remote=temp_remote, preserve_mode=True)
remote_device.sudo(f"mv {temp_remote} {remote_path}", pty=False)
print(f"File successfully uploaded to {remote_path}")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted this section to also have a remote_device.sudo('mkdir -p /tmp/support') and use the support folder instead just in case there happens to be stuff directly in /tmp that would be replaced, but that particular new directory consistently didn't let me send files to it for a permissions error
Just /tmp is fine

@SteveMicroNova
Copy link
Copy Markdown
Contributor Author

Lincoln has suggested using an SCP-style syntax for this

Convert device connection into a separate function

Upgrade file sending to be in line with file getting

regulate spacing between functions to two lines (there's been a mix of 1 and 2 lines so far)

Remove leftover comment

Add docstrings, typehinting
@linknum23 linknum23 requested review from linknum23 and removed request for rtertiaer March 3, 2025 16:35
@linknum23
Copy link
Copy Markdown
Collaborator

Lets see if I can test this soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grab arbitrary files from the remote device

2 participants