-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Quadlet - new unit type .login #26507
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
Conversation
[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore. |
004b64f
to
b0edc68
Compare
pkg/systemd/quadlet/quadlet.go
Outdated
|
||
stringKeys := map[string]string{ | ||
KeyCertDir: "--cert-dir", | ||
KeyPassword: "--password", |
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 is insecure so we should not do that as it is not any better than before, it is the same issue as I described in #26484 (comment).
Security relevant stuff should never be passed as on cli argument. I think what this must do is a pass --password-stdin
. Then in the systemd unit configure
StandardInput=data
StandardInputText=password
And lastly I am not sure on the perms of current generated files but we should make sure they are 600 so no other user could read the password from there.
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 is insecure
While I understand your comment, Quadlet already does something similar in .image
files: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html#creds.
I don't expect users to use it with real registries. But, do we need to block it? We can add a warning in the man page.
I think what this must do is a pass
--password-stdin
I don't see the benefit here. Isn't the password still leaked just under a different key? What am I missing?
And lastly I am not sure on the perms of current generated files but we should make sure they are 600 so no other user could read the password from there.
Currently, all service files are created with 644. Is your comment about permissions specific to the Password
key or as a whole to all .login
units?
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.
Yes but again this is horrible security practise so we must discourage it. The login unit should be a solution and not do the exact same error again. The issue is that running podman login --password mypw
leaks the password to all users who can view the process list which by default is everyone.
My solution is more secure because the password is never passed via cli argument but rather stdin which is "private". The permissions on the .login file are up to the user who creates them and only something quadlet could warn. But the permissions on the generated -login.service file is up to us so we must ensure the service file is only accessible by the same user because the service also has the password in clear text.
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.
Not sure I follow why using StandardInputText
is better. Doesn't it just mean that instead of:
[Login]
Password=password
The .login
unit will have:
[Login]
PasswordSTDIN=yes
[Service]
StandardInput=data
StandardInputText=password
Which also means that it will be copied to the .service
file
From what I can see, the actual podman login
command does not show in the journal. So, it's not leaked:
Jun 25 08:28:47 lima-default systemd[1001]: Starting image_test_t1-wmsnex2o-login.service...
Jun 25 08:28:47 lima-default image_test_t1-wmsnex2o-login[894538]: Login Succeeded!
Jun 25 08:28:47 lima-default systemd[1001]: Finished image_test_t1-wmsnex2o-login.service.
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.
Any user can just run ps auxww
and see all the process currently running and does the password is leaked to all users. For a short lived command such as podman login it is hard to actually hit the right window to see it but if you try hard enough and run ps in a loop you might get lucky. Certainly not a secure.
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.
OK. So, I'll remove the support for Password
and replace it with PasswordSTDIN
and document the requirement for setting StandardInputText
. Quadlet will not verify the existence of StandardInputText
when PasswordSTDIN
is set because it can be set in a drop-in.
As for permissions, the question is on the generated .service
files. Do you think that all .service
files generated for .login
units should have 600
? Only the ones with PasswordSTDIN
? Not change the implementation at all and warn the user in the doc?
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.
To be clear you don't have to remove support for "Password" as quadlet field. What I try to say here is that we (quadlet) internally map the Password to mean set --password-stdin and
[Service]
StandardInput=data
StandardInputText=<value of Password field>
The user should not need to worry about the implementation details but to me it is very important to not accidentally leak an important secret as cli arg.
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've adjusted the code, the tests and the documentation with your recommendations
# Use the same directory for all quadlet files to make sure later steps access previous ones | ||
mkdir $quadlet_tmpdir | ||
# Shutdown the login service to logout and let the dependency kick in | ||
service_cleanup $login_service inactive |
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 the podman login --authfile $authfile --get-login $registry
command you could run to check if you are logged into the registry. which should fail as the service stop should run logout
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.
Thanks. I added checked around starting and stopping of the login service (directly and indirectly)
|
||
Path of the authentication file. | ||
|
||
This field is mandatory when linking between an `AuthFile` key of a different unit and the `.login` unit. |
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.
would it make sense to instead default to %t/<unit-name>
?
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.
Good question, I'm not sure.
Not setting one, means that the login information is stored in the default file. So, units can depend on on the .login
unit (in the Wants
and After
) without setting AuthKey
.
But, maybe it is cleaner
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 is likely fine. Though I guess there is the gotcha that one cannot use unit specific specifiers like %n
as this would get resolved to a different name in the contianer unit where --authfile would be added. Though stuff like %t
would till be fine.
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.
My thought was that if the .login
file doesn't specify an AuthFile
then login info will be stored in the default file. So, the .container
does not need to specify an AuthFile
either, but it will need to specify the dependecy.
As for %n
, you are correct. I'll add it to the doc
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.
IMHO I don't see much benefit in forcing users to specify AuthFile
: Auth files don't have to be unique and can store the credentials of multiple registries (i.e. using the default auth file is totally file). Furthermore, a .login
unit doesn't have to be an appendix of a .container
unit: Just think about a global quay.io.login
unit (i.e. a .login
unit per registry, not per container, intentionally also working for podman run
on the shell). Let users decide how to use it.
It doesn't even mean that one must add an explicit dependency to .container
units manually then: It all depends on Systemd's execution tree, there are many ways to assert that the .login
unit is started before any .container
unit (e.g. with intermediary .target
units).
It should be noted in the docs though…
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.
Auth files don't have to be unique and can store the credentials of multiple registries (i.e. using the default auth file is totally file).
I think the main issue here is AFAICS there is no locking for writing the authfile. So if you actually use the same file in many login units they would all race against each other and potentially overwrite a previous login causing hard to find race conditions.
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's a valid point indeed. However, shouldn't this rather be solved by adding a note that users must keep that in mind and act accordingly? Neither a Quadlet-specific default value, nor forcing the user to specify AuthFile
manually solves this, because one could easily specify the same AuthFile
twice. It's basically the same as for ordering the .login
unit before the necessary .container
unit(s). Both should be noted in the docs though.
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 got me thinking: I don't know Quadlet's code, but since AuthFile=private-repo.login
in .container
units works, Quadlet must be aware of other Quadlet units when generating the actual Systemd units. So, how about grouping .login
units that attempt to write to the same auth file together and adding After=
directives to the generated Systemd units to ensure that they run sequentially? This would actually solve the issue with race conditions.
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 think the main issue here is AFAICS there is no locking for writing the authfile. So if you actually use the same file in many login units they would all race against each other and potentially overwrite a previous login causing hard to find race conditions.
https://github.com/containers/image/issues/1365 for the record.
84997b1
to
7d8e147
Compare
|
||
This field is mandatory when linking between an `AuthFile` key of a different unit and the `.login` unit. | ||
|
||
This is equivalent to the `--authfile` option. |
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 is equivalent to the `--authfile` option. | |
This is equivalent to the Podman `--authfile` option. |
In some places, you have Podman
for a line like this, in others, you do not. I prefer having Podman for each. However, your call, I'd just ask for consistency.
|
||
### `LogoutOnStop=` (defaults to `false`) | ||
|
||
When set to `true` the logout will be called when the service is stopped |
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.
When set to `true` the logout will be called when the service is stopped | |
When set to `true`, the logout will be called when the service is stopped. |
|
||
This key contains a list of arguments passed directly to the end of the `podman login` command | ||
in the generated file. It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware | ||
of what unexpected interactions can be caused by these arguments, is not recommended to use |
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.
of what unexpected interactions can be caused by these arguments, is not recommended to use | |
of the unexpected interactions that can be caused by these arguments, it is not recommended to use |
|
||
### `Secret=` | ||
|
||
Use a podman secret for the credentials |
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.
Use a podman secret for the credentials | |
Use a podman secret for the credentials. |
@TomSweeneyRedHat I made the requested changes and added an example |
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.
mostly LGTM just some minor comments
".login": { | ||
Extension: ".login", | ||
Order: 1, | ||
ServiceFileMode: os.FileMode(0600), |
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.
maybe a small comment here why this is special could help.
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.
Added
pkg/systemd/quadlet/quadlet.go
Outdated
".pod": 5, | ||
SupportedExtensions = map[string]ExtensionInfo{ | ||
".container": { | ||
Extension: ".container", |
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.
Why is Extension a field if the key has already the same value? I don't see where this would be needed and liek this it just feels like it is wasting a bit of memory.
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.
Initially I thought we might get the struct and pass it around and then the Extension
can be used. Since there's no usage of it, you're right and I removed it.
843c621
to
192b3cf
Compare
Implement a new unit for podman login Add support for AuthFile to container and kube units Allow linking between AuthFile and a login unit Signed-off-by: Ygal Blum <[email protected]>
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.
LGTM
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: Luap99, ygalblum The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@containers/podman-maintainers PTAL |
I don’t know. How can one ship a |
When I said "ship a As you wrote the |
I’m naive about these things. In my mental model, all of those Users work in that git repo, or whatever it is. Paths on the final machine don’t matter, the deployment scripts handle that. Or, alternatively, all the In none of these situations are users thinking about the paths at application deployment time. The paths are always a design-time concern, just like systemd unit names. |
PR needs rebase. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
@@ -1963,7 +1989,12 @@ This is equivalent to the Podman `--arch` option. | |||
|
|||
Path of the authentication file. | |||
|
|||
This is equivalent to the Podman `--authfile` option. | |||
This is equivalent to the `--authfile` option. |
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 is equivalent to the `--authfile` option. | |
This is equivalent to the Podman `--authfile` option. |
I'm not sure why "Podman" was dropped here, but not in other places?
|
||
### `Secret=` | ||
|
||
Use a podman secret for the credentials. |
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.
Use a podman secret for the credentials. | |
Use a Podman secret for the credentials. |
Two minor nits. Otherwise LGTM. Sorry about the lag, just back from PTO. |
And it looks like you need a rebase at this point to FWIW. |
@TomSweeneyRedHat Have read the discussion here? The question is do we need to that at all or would it not be better to point user to authfiles directly. |
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 didn't do a code review here, I rather just reviewed the proposed docs and want to join the discussion: I absolutely support this (great work @ygalblum btw ❤️).
To this day the only way to store registry credentials persistently without previous setup (like adding --authfile
to every single Podman command (which is particularly hard for auto-update
, see #12706 (comment)), or adding REGISTRY_AUTH_FILE
to the global environment) is the Docker fallback $HOME/.docker/config.json
. Also see rejected #12706.
Doing this with Quadlet .login
units is way more powerful: It e.g. allows one to easily manage different auth files for different containers and could play particularly well with #26462 and #26488.
|
||
Valid options for `[Login]` are listed below: | ||
|
||
| **[Login] options** | **podman login** | |
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.
| **[Login] options** | **podman login** | | |
| **[Login] options** | **podman login equivalent** | |
|
||
Path of the authentication file. | ||
|
||
This field is mandatory when linking between an `AuthFile` key of a different unit and the `.login` unit. |
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.
IMHO I don't see much benefit in forcing users to specify AuthFile
: Auth files don't have to be unique and can store the credentials of multiple registries (i.e. using the default auth file is totally file). Furthermore, a .login
unit doesn't have to be an appendix of a .container
unit: Just think about a global quay.io.login
unit (i.e. a .login
unit per registry, not per container, intentionally also working for podman run
on the shell). Let users decide how to use it.
It doesn't even mean that one must add an explicit dependency to .container
units manually then: It all depends on Systemd's execution tree, there are many ways to assert that the .login
unit is started before any .container
unit (e.g. with intermediary .target
units).
It should be noted in the docs though…
|
||
### `LogoutOnStop=` (defaults to `false`) | ||
|
||
When set to `true`, the logout will be called when the service is stopped. |
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.
When set to `true`, the logout will be called when the service is stopped. | |
When set to `true`, `podman logout` will be called when the service stops. |
Login files are named with a `.login` extension and contain a section `[Login]` describing the | ||
login command. The generated service is a one-time command that logs into a registry. | ||
|
||
Using login units allows pulling images from private registries without having to login manually. |
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.
A note about Systemd unit ordering should be added here (i.e. that users must ensure that the .login
unit runs before any .container
unit that wants to use these credentials starts - either manually, or by using AuthFile=private-repo.login
in the .container
unit)
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.
Even though it might be a little off-topic, hinting users into the right direction might be helpful. How about something like the following:
Note that Systemd generally starts units in parallel. This can cause ordering issues, because a
.container
unit can't pull images from an authenticated source unless the.login
unit with the necessary credentials runs already. Quadlet automatically ensures proper order when addingAuthFile=private-repo.login
to a.container
unit. However, else one must either add properAfter=
directives (e.g.After=private-repo-login.service
) to the.container
units manually, or use a custom synchronisation point like creating acontainers.target
unit and addingBefore=containers.target
to all.login
units andAfter=containers.target
to all.container
units.
@PhrozenByte How is creating/distributing a login file easier/better is your eyes than creating/distributing the authfile directly? Because that is what the argument is about here. You can already create a authfile once and ship it next to the quadlet file and refer to it in the container unit. |
@Luap99 , I think the .login file is easier for end users to consume and digest. The @ygalblum users most likely do not care about podman and its command line arguments and they do not understand what the authfile even is. They want to run the containerized service the easiest way possible and the podman and quadlets are just implementation details. The easiest way most likely is downloading the quadlets with instruction to "edit the .login file and change your username/password there". People are used to edit config files like that and there are much more users used to work with systemd than users used to work with podman (just guessing?)? The authfile way is probably more complex, because the users have to run the "podman login --authfile". This is not the usual command people run and remember to re-run in case the username/pass changes and for @ygalblum , it is harder to find out sane "default path" which would work across different distributions. Or is there some default path for authfiles which can be used already? If it's up to user to find out good place where to store the authfile, the user also has to change other systemd files to set the AuthFile to right location. This is yet another step. I think the workaround could be just using /opt/myapp/authfile and let the user run podman login --authfile /opt/myapp/authfile. Not sure what prevents @ygalblum to do that. Edit: There is definitely not a default authfile per systemd service :-). @mtrmac is right that if there is some install.sh or ansible involved, nothing from that matter anymore most likely, because you can just script username/password or authfile creation and make it an implementation detail for a user as well. I also like that this could in the future allow passing authfile as systemd credentials. |
@Luap99 Multiple advantages actually:
|
Adding a dependency on
That’s an anti-feature. E.g. the Docker Hub registry is, over time, slowly but seemingly purposefully, moving towards a model where authentication happens via OAuth, perhaps using a browser. Also, many of the cloud-specific registry services similarly require a cloud credential and a cloud-specific tool to log in. It seems fairly unlikely that users editing files with static credentials will remain the default, or even possible, workflow, long-term. A tool will have to be involved.
I don’t know how reading a man page and writing a file, vs. reading a man page and running a command, is substantially different, but I’ll defer to UI experts on this.
FWIW Skopeo would suffice, the
Yes; I think that’s an interesting UI/design/workflow concern, and it would be valuable to figure this out. (Not just for passphrases, but for any other files, e.g. configuration one might want to mount into a vendor-provided otherwise-unmodified container image.)
The current design which contains a single credential to be written to an exclusively-owned authfile is already insufficient for use with mirrors.
N/A. Just point
That could be a future feature without ever providing a “create a systemd unit with a password in it” feature as proposed here. |
Not necessarily: Systemd uses
Can you please elaborate on how an OAuth workflow involving a browser is supposed to work on a server? I assume this is a misunderstanding: OAuth is solely used to get access tokens. Access tokens are still "static credentials". If the access tokens are long-living, one simply deploys them to the server as before. If they are short-living, we got a strong argument pro
Please elaborate.
systemd-creds is supposed to store credentials, not config files. A auth provider doesn't know Podman's auth file format, but just returns the credentials. It's up to
I'm confused. You vote for not introducing a Or do you solely oppose the |
I meant adding
Yes, but explicitly created using a tool, perhaps using a sequence of HTTP requests. So the workflow is
How do I create an (Yes, in that other thread there is a discussion about using multiple
I’m confused about which computer is the "client" and which one is the "server" here. On which one are the Quadlet units deployed? On which one are they set up / maintained / edited, to be deployed to the other one?
That one I oppose strongly, and I wrote why (separation of secrets vs. sensitive-but-not-secret configuration). And overall I think the “one credential is necessary” mental model is incorrect, but that’s a weaker objection. |
Let's call "server" the machine the Quadlet units are deployed to, and "client" the machine that is responsible for preparing and deploying the config to the "server".
First, I don't think that Second, the whole idea of So, in the end it's more like running
By using three
I honestly don't see much of a difference whether one deploys a
I honestly still don't understand how that opposes running |
I don't think Quadlet can do it on its own. How will it know the order? Or do you mean that the order itself does not matter as long as they are not executed in parallel? |
The latter. Order doesn't matter, only that multiple I don't know Go, but in other languages I'd simply group the |
I see. It will require some additional code in the way Quadlet handles the dependency between files. But, it should be doable. Having said that, I want to add that before I make any changes I need to get an approval on the premise of this feature. No point for me to continue working on this if it will never get approved |
The files (well, “credentials in
When running “login” on the server is unnecessary, to me that’s already a pretty strong reason not to design workflows that require or recommend it. Not a decisive reason, but there better be very significant benefits to designing things that way. Personally I find the idea that, to support 3 mirrors, I must create 3 separate |
I don't really see a reason why one would want to add
Think about e.g. TPMs. But that's not the point. The point is that running
My reason is exactly the same, just the other way round: When running Again, I might not even have Podman installed on the machine that deploys my servers (the "client"). Requiring users to install Podman there even though they simply don't need it is a bad thing and anything but "simple".
We've already discussed this, you don't have to do either one. Please see above. Besides, adding a Running |
If you mean that the *sigh* maybe I really should just let this be and let people who like this kind of stuff build Quadlet how they like? |
First, as you mentioned, the only reason Quadlet would need to maintain an order is because of a bug in Second, I guess I hold the opposite view. Solve something for the 80% and let the 20% worry about their complex issues. For sure, I wouldn't block the solution for the 80% because it doesn't fit all the different options.
Quadlet is not about deciding for users how they should use podman commands (we tried that in the beginning and ended up removing almost all opinions). If users want to use this directive, they should be able to do that along with the consequences. |
A friendly reminder that this PR had no activity for 30 days. |
Closing this PR following feedback from the containers team. Can be revisited later if there's any interest. |
Does this PR introduce a user-facing change?
Yes