-
Notifications
You must be signed in to change notification settings - Fork 18
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
feat: Add support of configuring host to use Satellite server #170
base: main
Are you sure you want to change the base?
Conversation
jirihnidek
commented
Nov 21, 2024
- Card ID: CCT-993
- Introduced new command "configure" and immediately added sub-command "satellite" to this command. "satellite" subcommand has --url CLI option and you can set hostname or URL of server Satellite server.
- When valid URL or hostname is provided, then rhc tries to read /api/ping endpoint and it tries to verify that given server is Satellite server
- When Satellite server is verified, then it tries to download bootstrap script from /pub/katello-rhsm-consumer
- When script is downloaded, then this script is run as root user.
- Is little bit risky to run some script downloaded from web server. For this reason we will try to use different approach in the future. Satellite server will provide public REST API endpoints providing CA certs and rendered rhsm.conf. We will simply download these files and install them to system. Unfortunately, these REST API endpoints will be available in some future version of Satellite.
I'm not sure about following things:
TODO:
|
48699ce
to
409c595
Compare
/retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not read over satellite.go
yet. I'll get to that in a follow up review. I left a scattering of comments through the other files. I have not tested any of the functionality yet. This is just from a reading over of the code.
s.Suffix = fmt.Sprintf(" Configuring '%v' to use Satellite server: %v", hostname, satelliteUrl.Host) | ||
} | ||
// Run the script. It should install CA certificate, change configuration of rhsm.conf. | ||
// In theory, it can do almost anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scary. We should follow this up with a distributed SELinux policy to restrict the scope execution for rhc
in general.
I would also like to take a UX pass over the strings in the new command. We should ensure consistent terminology as much as we can. |
409c595
to
2413479
Compare
OK, I updated the PR. |
Hi, Can we be sure to support multiple activation keys when registering to Satellite? |
Hi Chris, You can use CLI option
It is also emphasize in the help of
|
2413479
to
c69b666
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The satellite.go
code feels disjointed. I think we could clean up the function pattern by making a dedicated HTTP client and building some methods on that. This way the two requests we use today (ping and /pub/katello-rhsm-consumer) can share the same HTTP client (TLS configuration, base URL, etc).
Then this client can be instantiated from within satelliteAction
and used to perform the various operations it wants to.
The ping method feels incomplete too. If the response body is not documented on the Internet somewhere, I don't like unmarshalling all those fields, especially if they are not used. We could probably get away with only a status field, if we're checking for liveness by sending the server a ping.
configure_cmd.go
Outdated
configureSatelliteResult.HostnameError = err.Error() | ||
return cli.Exit(configureSatelliteResult, exitCode) | ||
} else { | ||
return cli.Exit(err, exitCode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a wrapping error message here?
return cli.Exit(err, exitCode) | |
return cli.Exit(fmt.Errorf("could not acquire hostname: %w", err), exitCode) |
satellite.go
Outdated
} | ||
|
||
// Error implement error interface for structure ConfigureSatelliteResult | ||
func (configureSatelliteResult ConfigureSatelliteResult) Error() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a Go convention to refer to the 'self' object in a method as a single
or small variable name. I think this could be made a bit more concise by
replacing configureSatelliteResult
with result
. Maybe then rename the
existing result
string to message
or something like that.
satellite.go
Outdated
} | ||
transport := http.DefaultTransport.(*http.Transport).Clone() | ||
transport.TLSClientConfig = &tlsConfig | ||
client := &http.Client{Transport: transport} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this same client (with its TLS configuration) not required to download the
script in downloadSatelliteScript
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that it is possible to access /pub
using (http and https). It is possible to access /api/ping
using only https. In theory we can try to use one client using only one https connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I think we can consolidate the HTTP clients into a specialized one for
performing Satellite REST requests.
satellite.go
Outdated
return fmt.Errorf("unable to parse ping satellite server response: %v", err) | ||
} | ||
|
||
// In theory, we could check for the status of candlepin and candlepin auth, etc., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it safe to assume that if the HTTP server responds with 200, that's an
appropriate response to a "ping" request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I can delete this commented block of code.
- In theory we can also remove
json.Unmarshal()
of the body, but as I already said, it is safer to do more checks that the server is really Satellite server than do less checks, because we run downloaded script as a root.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If user provide full URL to some no-satellite script and there will be also REST API endpoint /api/ping
(not so unlikely), then the script will be downloaded and run as a root user. For this reason I would rather check that returned object of /api/ping
is really response from Satellite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to me. Considering the risk we're taking here (downloading a
shell script and running it as root), we should do as much as we can to ensure
that this script is coming from a trusted source.
Do you mean to reuse this https://github.com/RedHatInsights/rhc/blob/main/internal/http/client.go again? It seems that this file is unused ATM. It will probably require to extend this file to be able to use it for communication with Satellite server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
satellite.go
code feels disjointed. I think we could clean up the function pattern by making a dedicated HTTP client and building some methods on that. This way the two requests we use today (ping and /pub/katello-rhsm-consumer) can share the same HTTP client (TLS configuration, base URL, etc).Do you mean to reuse this https://github.com/RedHatInsights/rhc/blob/main/internal/http/client.go again? It seems that this file is unused ATM. It will probably require to extend this file to be able to use it for communication with Satellite server.
Maybe not reuse it directly, but create a SatelliteHTTPClient
type that preconfigures the TLS configuration used by a specific backing HTTP client, and that has methods attached to it to make the REST API calls.
type SatelliteHTTPClient struct {
client *http.Client
}
func (s *SatelliteHTTPClient) Ping() (SatelliteHTTPPingResponse, error) { ... }
It might seem like a lot more boilerplate code up front, but I think it will position this command to evolve more Satellite features in the future without needing to refactor things.
c69b666
to
9e0dcaf
Compare
@@ -39,6 +39,7 @@ var ( | |||
SysconfDir string | |||
LocalstateDir string | |||
DbusInterfacesDir string | |||
VarLibDir string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's something about this name that doesn't sit right with me. "VarLibDir" makes me think /var/lib
, not /var/lib/rhc
. I'd rather we name it something more accurate, like RhcLocalstateDir
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lint error caught by the linting actions I noticed, and I left a few comments here and there. I'll get to some functional testing shortly.
// We have to use insecure HTTPs connection, because most of the customers use | ||
// self-signed certificates | ||
tlsConfig := tls.Config{ | ||
InsecureSkipVerify: true, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we're downloading a shell script from a URL and executing it as root, and we're downloading that shell script from an unsigned HTTPS endpoint. 😱
Do customers actually use self-signed certificates? Or do they use certificates that are signed by an internal/private signing authority? Could we expose the option to add a CA root certificate as part of this TLS configuration so that customers can at least use their own signing authority? And maybe make them opt-in to fully disabling the certificate verification?
return &satellitePingResponse, nil | ||
} | ||
|
||
// downloadSatelliteScript tries to download script from provided URL to given file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method documentation looks like a leftover from the previous implementation. It doesn't match the current method name and it describes a parameter that it no longer accepts.
// downloadSatelliteScript tries to download script from provided URL to given file | |
// downloadScript tries to download script from provided URL to given file |
} | ||
|
||
satelliteUrlStr := ctx.String("url") | ||
if satelliteUrlStr == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can mark the cli.Flag
defining this flag as Required: true
. That makes this check unnecessary.
&cli.StringFlag{ | ||
Name: "url", | ||
Usage: "URL of the Satellite server", | ||
Aliases: []string{"u"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This flag should be marked as required:
Aliases: []string{"u"}, | |
Aliases: []string{"u"}, | |
Required: true, |
return fmt.Errorf("cannot configure connected system to use satellite; disconnect system firtst") | ||
} | ||
|
||
return checkForUnknownArgs(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if I've commented on this before, but I like this pattern of returning a call through another function. This function is named very well and this clearly describes what happens through this function. Nicely written.
@@ -174,6 +174,40 @@ func main() { | |||
Before: beforeConnectAction, | |||
Action: connectAction, | |||
}, | |||
{ | |||
Name: "configure", | |||
Usage: "Configure the host", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this text surrounded by the other text in the help output, I think
this needs to change to something like:
"Configures the behavior of the system"
COMMANDS:
connect Connects the system to Red Hat
configure Configure the host
disconnect Disconnects the system from Red Hat
status Prints status of the system's connection to Red Hat
help, h Shows a list of commands or help for one command
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts on the user experience.
I definintely don't like the insecure certificate experience. I first set up my instance like so: rhc configure satellite --url https://my-satelite.local
and got the error back:
could not download satellite bootstrap script: could not download file https://my-satellite.local/pub/katello-rhsm-consumer : Get "https://my-satellite.local/pub/katello-rhsm-consumer": tls: failed to verify certificate: x509: certificate signed by unknown authority
We need to expect users will encounter this, and should provide two possible solutions:
- The ability to add a certificate authority to the TLS configuration so that the server certificate is valid (
--ca-root
) - The ability to opt-out of all security (
--insecure
)
I'm not pleased with the idea of a (2), but I know our options are limited with the scope of host identification we get from Satellite currently. That being said, if we are going to allow for the insecure downloading of content, Id' much rather we do so with explicit opt-in from the user.
My execution of the script failed. All I got on the console was: execution of 0x40001eeab0 script failed: exit status 127
Unless we know this script is robust enough to never fail, we really need to provide a means for users to diagnose these errors. At the very least, let's pipe the stdout and stderr of the script to a log file. This error message isn't descriptive enough for a user to start troubleshooting.
* Card ID: CCT-993 * Introduced new command "configure" and immediately added sub-command "satellite" to this command. "satellite" subcommand has --url CLI option and you can set hostname or URL of server Satellite server. * When valid URL or hostname is provided, then rhc tries to read /api/ping endpoint and it tries to verify that given server is Satellite server * When Satellite server is verified, then it tries to download bootstrap script from /pub/katello-rhsm-consumer to directory /var/lib/rhc * When script is downloaded, then this script is run as root user. The "/usr/bin/bash" is used for running script. * When running of script is finished, then this script is deleted unless hidden CLI option --keep-artifacts is used * It is not possible to run this command, when system is connected, because it would not be possible tounregister or disconnect system, if configuring was allowed on connected system. The behavior of rhc/subscription-manager would be strange * It is little bit risky to run some script downloaded from web server. For this reason we will try to use different approach in the future. Satellite server will provide public REST API endpoints providing CA certs and rendered rhsm.conf. We will simply download these files and install them to system. Unfortunately, these REST API endpoints will be available in some future version of Satellite. * Directory /var/lib/rhc is created during installation of RPM package
9e0dcaf
to
974ef6d
Compare