Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new plugin to allow SCP config backups to a remote host. #458

Merged
merged 1 commit into from
Jan 3, 2018
Merged

A new plugin to allow SCP config backups to a remote host. #458

merged 1 commit into from
Jan 3, 2018

Conversation

dharrigan
Copy link
Member

This simple plugin takes a few parameters, such as hostname and username and
schedules a cron job to remotely scp the config.xml file at regular intervals.
It uses the public/private keypair of the built-in root user as the source of
the key exchange. This means that the public key must be copied to the remote
host and added to the authorized_keys file (for the defined user).

The remote file is backed up as config-YYYY-DD-MM-HH-MM.xml, for example:
config-2018-01-02-15-40.xml. It's possible to change the remote location of
where the config file is backed up to.

The cron job can be modified under System/Settings/Cron and the schedule
adjusted to suit the backup frequency requirements.

-=david=-

closes #457

Copy link
Member

@fabianfrz fabianfrz left a comment

Choose a reason for hiding this comment

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

In my opinion this should not be here but included into the core page for backups. As long as there is a RCE this must not be merged.

See comments for other stuff.

}

$remoteLocationFullPath = $remoteLocation . "config-" . date('Y-m-d-H-i') . ".xml";
$command = "scp -P $port -i $identifyFile /conf/config.xml $username@$hostname:$remoteLocationFullPath";
Copy link
Member

Choose a reason for hiding this comment

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

Looks like RCE

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't believe it is - can you show if it is? Happy to change if it's provable. I do escape the command as per PHP guidelines.

Copy link
Member

Choose a reason for hiding this comment

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

You are using data from config.xml unescaped in the shell command - escape them with https://secure.php.net/manual/en/function.escapeshellarg.php

the variables that need escaping are

  • $username
  • $hostname
  • $remoteLocationFullPath

Copy link
Member Author

Choose a reason for hiding this comment

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

Fantastic! Thank you. I'll do that :-)

-=david=-

}
}

exit(0);
Copy link
Member

Choose a reason for hiding this comment

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

not needed

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

if(isset($scpBackup) && isset($scpBackup->enabled) && $scpBackup->enabled == 1) {
$hostname = $scpBackup->hostname;
$port = empty(trim($scpBackup->port)) ? "22" : $scpBackup->port;
$username = empty(trim($scpBackup->username)) ? "root" : $scpBackup->username;
Copy link
Member

Choose a reason for hiding this comment

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

usernames cannot include whitespace

Copy link
Member Author

Choose a reason for hiding this comment

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

That is true, however, someone may submit the form with a blank username, thus doing additional check here just to be safe and sure and to default to "root" if nothing is supplied.

Copy link
Member

Choose a reason for hiding this comment

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

yes but you won't need trim if you would do proper validation

<items>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
Copy link
Member

Choose a reason for hiding this comment

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

indent issue

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

$result = array();
if ($this->request->isGet()) {
$mdlGeneral = $this->getModel();
$publicKey = fopen("/conf/sshd/ssh_host_rsa_key.pub", "r");
Copy link
Member

Choose a reason for hiding this comment

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

this might be the wrong key - it is only or the server to show its identity to the client. It should not be used for that.

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe it is the correct key, as this is the only available to me - "root" on OPNsense doesn't have a default generated public/private keypair - Fitch pointed me to this particular file.

PLUGIN_NAME= scp-backup
PLUGIN_VERSION= 1.0.0
PLUGIN_COMMENT= Perform config backups using SCP.
PLUGIN_MAINTAINER= dharrigan@gmail.com
Copy link
Member

Choose a reason for hiding this comment

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

Should be devel first

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated as per Fitch's comment below.

</field>
<field>
<id>general.remoteLocation</id>
<label>Remote Location</label>
Copy link
Member

Choose a reason for hiding this comment

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

Directory

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

</field>
<field>
<id>general.publicKey</id>
<label>Local Root's Public Key</label>
Copy link
Member

Choose a reason for hiding this comment

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

Should not be the one of root or anyone else. Should be an autogenerated key for this service. Should also support passwords as well.

Maybe the fingerprint should be configurable as well for the remote server.

Copy link
Member Author

Choose a reason for hiding this comment

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

This was agreed with Fitch.

Happy to accept PR's for passwords, fingerprints etc...

</username>
<remoteLocation type="TextField">
<Required>N</Required>
<ValidationMessage>Please provide a remote location.</ValidationMessage>
Copy link
Member

Choose a reason for hiding this comment

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

directory

Copy link
Member Author

Choose a reason for hiding this comment

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

Happy to change it to RemoteDirectory :-)

Copy link
Member

Choose a reason for hiding this comment

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

please no camelcase in strings - that's already hard to read in the code ;)

<Required>N</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<ValidationMessage>Please provide a valid port number between 1 and 65535. Port 22 is usually the default.</ValidationMessage>
Copy link
Member

Choose a reason for hiding this comment

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

remove usually

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@fichtner fichtner self-assigned this Jan 2, 2018
@fichtner
Copy link
Member

fichtner commented Jan 2, 2018

@dharrigan thanks for this, I'll do further review tomorrow!

Just a thought: this is probably core-worthy, especially since it has no external dependencies. As such it would be better to start with PLUGIN_DEVEL and a version 0.x so we can add it to the plugins, but have a bit of time to decide what we are going to do with it mid-term. How does that sound? :)

@dharrigan
Copy link
Member Author

Hi,

Sure, I can update the version. Fabian has raised some points, a lot of which you and I have already discussed off-line privately. Can we co-ordinate on this?

-=david=-

@dharrigan
Copy link
Member Author

dharrigan commented Jan 2, 2018 via email

@fabianfrz
Copy link
Member

fabianfrz commented Jan 2, 2018

@dharrigan you can do it however you like but you should provide a mask in any case. This is for no whitespace for example: /\S*/

For usernames it is probably more complex like characters, numbers and a limited set of special chars (dot, dash, underscore, maybe plus) ->/^[a-z0-9\.\-_\+]*$/

@dharrigan
Copy link
Member Author

dharrigan commented Jan 2, 2018 via email

@dharrigan
Copy link
Member Author

Hi,

Feedback applied and new push made. I await feedback :-)

-=david=-

@fichtner fichtner assigned dharrigan and unassigned fichtner Jan 3, 2018
Copy link
Member

@fichtner fichtner left a comment

Choose a reason for hiding this comment

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

looks good, but as discussed we will rewrite the backup script to use shell and a dedicated config file.

maybe the cron job handling can be altered as well using the plugin cron hook and a simple setting...

but none of this is pressing. we're aiming for inclusion in an early 18.1.x release.

@dharrigan
Copy link
Member Author

dharrigan commented Jan 3, 2018

Hi @fabianfrz,

I've spoken with @fichtner. He's happy enough with what I've done so far and also with the updates based upon your great review yesterday. He would me to merge in if you're happy - he has said that he's going to rewrite the code slightly to externalise the configuration of the plugin into its own configuration file and to re-write the cron'ed PHP script to be a shell script. He'll do that once this branch has been merged into master.

Are you happy enough with the code for me to merge in? You can always reach out to me on IRC if you want to go over a few things :-)

Speak to you later.

-=david=-

Copy link
Member

@fabianfrz fabianfrz left a comment

Choose a reason for hiding this comment

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

Only some minor issues left - It's OK now.

$("#saveAct").click(function() {
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
saveFormToEndpoint(url="/api/scpbackup/general/set", formid='frm_general_settings', null);
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
Copy link
Member

Choose a reason for hiding this comment

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

this will probably not work because it's async - @fichtner this is for you

Copy link
Member

Choose a reason for hiding this comment

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

nice catch, it needs a return function :)


$command = "scp -P $port -i $identifyFile $configFile $username@$hostname:$remoteDirectoryFullPath";

syslog(LOG_WARNING, "scp_backup command: $command");
Copy link
Member

Choose a reason for hiding this comment

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

  • warning?
  • is this needed - looks like a debug code which is still left

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi, I tried LOG_INFO, but nothing came out on the syslog. I wanted to print out the command being executed, just in case something was blowing up and to help out the user. LOG_ERR seemed inappropriate :)

Copy link
Member

Choose a reason for hiding this comment

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

I would say this is LOG_DEBUG. However it is not risky because it does not spam the log.

@dharrigan
Copy link
Member Author

Thank's @fabianfrz, I'll merge in shortly.

-=david=-

@dharrigan
Copy link
Member Author

dharrigan commented Jan 3, 2018 via email

This simple plugin takes a few parameters, such as hostname and username and
schedules a cron job to remotely scp the config.xml file at regular intervals.
It uses the public/private keypair of the built-in root user as the source of
the key exchange. This means that the public key must be copied to the remote
host and added to the authorized_keys file (for the defined user).

The remote file is backed up as `config-YYYY-DD-MM-HH-MM.xml`, for example:
`config-2018-01-02-15-40.xml`. It's possible to change the remote location of
where the config file is backed up to.

The cron job can be modified under System/Settings/Cron and the schedule
adjusted to suit the backup frequency requirements.

-=david=-

closes #457
@fabianfrz fabianfrz added the feature Adding new functionality label Jan 3, 2018
@dharrigan dharrigan merged commit e59ee70 into opnsense:master Jan 3, 2018
@dharrigan dharrigan deleted the scp-backup branch January 3, 2018 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Adding new functionality
Development

Successfully merging this pull request may close these issues.

sysutils/scp-backup: A new plugin to allow SCP backups of the configuration file to a remote host.
3 participants