You are not logged in.

#1 2021-04-04 12:10:21

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

[SOLVED] PKGBUILD RFC: tinc-hardened

Hi.

I'm playing with systemd-assisted tinc sandboxing since I've realised I'm not comfortable having tinc running as root.

This is what I've ended up with:

[Unit]
Description=Tinc net %i
Documentation=info:tinc
Documentation=man:tinc(8) man:tinc.conf(5)
Documentation=http://tinc-vpn.org/docs/
PartOf=tinc.service
ReloadPropagatedFrom=tinc.service

[Service]
Type=simple
User=tinc
Group=tinc
DynamicUser=true
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
DeviceAllow=/dev/net/tun rw
NoNewPrivileges=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=yes
ProtectProc=invisible
ProcSubset=pid
ProtectSystem=strict
RestrictSUIDSGID=true
SystemCallArchitectures=native
RestrictRealtime=true
LockPersonality=true
MemoryDenyWriteExecute=true
RemoveIPC=true
ProtectHostname=true
SystemCallFilter=@system-service
SystemCallFilter=~@resources @privileged
UMask=066
WorkingDirectory=/var/lib/tinc
ConfigurationDirectory=tinc/%i
RuntimeDirectory=tinc
StateDirectory=tinc
LogsDirectory=tinc
ExecStartPre=+cp -r /etc/tinc/%i /var/lib/tinc/%i
ExecStartPre=+chown -R tinc: /var/lib/tinc/%i
ExecStart=/usr/bin/tincd -D -c /var/lib/tinc/%i --logfile=/var/log/tinc/%i.log --pidfile=/run/tinc/%i.pid
PIDFile=/run/tinc/%i.pid
ExecReload=/usr/bin/tincd -kHUP -c /var/lib/tinc/%i --logfile=/var/log/tinc/%i.log --pidfile=/run/tinc/%i.pid
ExecStopPost=+rm -rf /var/lib/tinc/%i

[Install]
WantedBy=tinc.service

I'd like to preserve DynamicUser, but I don't want to chown files in /etc on unit startup, hence this mess with ExecStartPre. If you know how to do it in a more elegant way, please let me know. Other suggestions and comments are also welcome.

Thanks.

Last edited by post-factum (2021-04-06 20:27:37)


uname == latest pf-kernel

Offline

#2 2021-04-04 19:13:01

respiranto
Member
Registered: 2015-05-15
Posts: 479
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

Am I correct in assuming that there are non-world-readable files in the ConfigurationDirectory?
Or do you need write access to some of these files?

Why do you want DynamicUser, given that User and Group do not depend on %i (which might be useful though)?


Other comments:

The setting `UMask=066' seems unusual to me.  Why not `077'?

Note that defining RuntimeDirectory et al. sets corresponding environment variables (see systemd.exec(5)) which can be used in other definitions as $VAR or ${VAR} (see systemd.service(5)).
Also, I'd probably set `StateDirectory=tinc/%i'.

Offline

#3 2021-04-04 19:24:19

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

respiranto wrote:

Am I correct in assuming that there are non-world-readable files in the ConfigurationDirectory?

Yes, generally speaking for tinc there is at least one file with a private key that should be readable by root (and tinc user) only.

respiranto wrote:

Or do you need write access to some of these files?

AFAIK, no. tinc doesn't write anything to /etc as per my knowledge.

respiranto wrote:

Why do you want DynamicUser, given that User and Group do not depend on %i (which might be useful though)?

To isolate tincd from tasks that are running on behalf of other users. Or do you think that running under the root user is fine as long as other restrictions are in place?

EDIT: I also do not want to deal with permanent users if there's no strong need. Although that might make things easier permission-wise.

respiranto wrote:

The setting `UMask=066' seems unusual to me.  Why not `077'?

IIUC, tinc has no need to create executable files.

respiranto wrote:

Note that defining RuntimeDirectory et al. sets corresponding environment variables (see systemd.exec(5)) which can be used in other definitions as $VAR or ${VAR} (see systemd.service(5)).

I believe you are suggesting this in order to avoid hard-coded paths in ExecStart & co? If so, this is a good one, I'll take a look, thanks.

respiranto wrote:

Also, I'd probably set `StateDirectory=tinc/%i'.

Yes, maybe. Is this just a nit, or it has some implications with regard to ownership of the files?

Last edited by post-factum (2021-04-04 19:29:35)


uname == latest pf-kernel

Offline

#4 2021-04-04 20:31:42

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

Second try, simplified:

[Unit]
Description=Tinc net %i
Documentation=info:tinc
Documentation=man:tinc(8) man:tinc.conf(5)
Documentation=http://tinc-vpn.org/docs/
PartOf=tinc.service
ReloadPropagatedFrom=tinc.service

[Service]
User=tinc
Group=tinc
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
DeviceAllow=/dev/net/tun rw
NoNewPrivileges=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=yes
ProtectProc=invisible
ProcSubset=pid
ProtectSystem=strict
RestrictSUIDSGID=true
SystemCallArchitectures=native
RestrictRealtime=true
LockPersonality=true
MemoryDenyWriteExecute=true
RemoveIPC=true
ProtectHostname=true
SystemCallFilter=@system-service
SystemCallFilter=~@resources @privileged
UMask=066
WorkingDirectory=/var/lib/tinc
ConfigurationDirectory=tinc/%i
ConfigurationDirectoryMode=500
RuntimeDirectory=tinc
StateDirectory=tinc
ExecStart=/usr/bin/tincd -D -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/%i.pid
PIDFile=${RUNTIME_DIRECTORY}/%i.pid
ExecReload=/usr/bin/tincd -kHUP -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/%i.pid

[Install]
WantedBy=tinc.service

and:

u tinc - "tinc user" /var/lib/tinc -

uname == latest pf-kernel

Offline

#5 2021-04-04 20:50:28

respiranto
Member
Registered: 2015-05-15
Posts: 479
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

post-factum wrote:
respiranto wrote:

Am I correct in assuming that there are non-world-readable files in the ConfigurationDirectory?

Yes, generally speaking for tinc there is at least one file with a private key that should be readable by root (and tinc user) only.

Then I do not know of a better solution, if DynamicUser is desired.
However, I suggest copying the configuration onto a temporary filesystem, to ensure the secret data remains secret.  Either just somwhere into /tmp or /var/tmp (given that PrivateTmp=yes), or via TemporaryFilesystem= (see systemd.exec(5)).  This would basically replace the ExecStopPost= command.

post-factum wrote:
respiranto wrote:

Why do you want DynamicUser, given that User and Group do not depend on %i (which might be useful though)?

To isolate tincd from tasks that are running on behalf of other users. Or do you think that running under the root user is fine as long as other restrictions are in place?

Running as non-root user is generally a very good idea.  (Though, if I understand this correctly, the AmbientCapabilities give the chosen non-root user mostly the same rights as root itself would have.)
User namespaces might also help, but systemd service files do not seem to provide this ability.

post-factum wrote:

EDIT: I also do not want to deal with permanent users if there's no strong need. Although that might make things easier permission-wise.

That's what I had in mind.  User= and Group= without DynamicUser=.

post-factum wrote:
respiranto wrote:

The setting `UMask=066' seems unusual to me.  Why not `077'?

IIUC, tinc has no need to create executable files.

So why not mask the execute bits?

post-factum wrote:
respiranto wrote:

Also, I'd probably set `StateDirectory=tinc/%i'.

Yes, maybe. Is this just a nit, or it has some implications with regard to ownership of the files?

The latter.  Which probably does not matter.  Currently, `/var/lib/tinc' whould be owned by `tinc', otherwise by `root'.
Again, I'd probably set `User=tinc-%i', in which case it would actually matter.  (Actually, all *Directories, except for ConfigurationDirectories would need to be distinguishable by %i).


On the second try:
I assume you changed the ownership of some files in `/etc/tinc'.
Concerning the assumed sysusers.d configuration, it would be good to know what happens if it was ever deleted; that is, if the user would be deleted and the UID reassignable, in which case the secret data would be less secret.

Offline

#6 2021-04-04 21:05:00

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

respiranto wrote:

Then I do not know of a better solution, if DynamicUser is desired.
However, I suggest copying the configuration onto a temporary filesystem, to ensure the secret data remains secret.  Either just somwhere into /tmp or /var/tmp (given that PrivateTmp=yes), or via TemporaryFilesystem= (see systemd.exec(5)).  This would basically replace the ExecStopPost= command.

Good one. Let it stay for history in case DynamicUser pops up again, but I've found it is too much hassle to deal with it, and I'm opting in for a static user instead.

respiranto wrote:

Running as non-root user is generally a very good idea.  (Though, if I understand this correctly, the AmbientCapabilities give the chosen non-root user mostly the same rights as root itself would have.)

Still, capabilities restrict the user to certain operations only.

respiranto wrote:

User namespaces might also help, but systemd service files do not seem to provide this ability.

PrivateUsers=, I believe? Although that breaks DeviceAllow= somehow.

respiranto wrote:

That's what I had in mind.  User= and Group= without DynamicUser=.

It seems it is really easier.

respiranto wrote:

So why not mask the execute bits?

066 is what masks execute bits, no? But I've read that default Linux permissions would exclude execution bits for files anyway, so UMask might just be redundand.

respiranto wrote:

The latter.  Which probably does not matter.  Currently, `/var/lib/tinc' whould be owned by `tinc', otherwise by `root'.
Again, I'd probably set `User=tinc-%i', in which case it would actually matter.  (Actually, all *Directories, except for ConfigurationDirectories would need to be distinguishable by %i).

Ack. Having the user templated is appealing, but that brings me back to DynamicUser= and all the associated mess with it. I have to think more about it.

respiranto wrote:

On the second try:
I assume you changed the ownership of some files in `/etc/tinc'.

Yes.

respiranto wrote:

Concerning the assumed sysusers.d configuration, it would be good to know what happens if it was ever deleted; that is, if the user would be deleted and the UID reassignable, in which case the secret data would be less secret.

I somewhat understand this concern, but removing the user is a superuser operation, in which case the system might be already doomed.


uname == latest pf-kernel

Offline

#7 2021-04-05 08:24:13

respiranto
Member
Registered: 2015-05-15
Posts: 479
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

post-factum wrote:
respiranto wrote:

User namespaces might also help, but systemd service files do not seem to provide this ability.

PrivateUsers=, I believe? Although that breaks DeviceAllow= somehow.

1) Yes, I missed that somehow.  I thought to remember it was possible.
2) That makes sense (unfortunately?).

post-factum wrote:

066 is what masks execute bits, no? But I've read that default Linux permissions would exclude execution bits for files anyway, so UMask might just be redundand.

6 ~ 110 ~ rwx.
The default UMask is usually 0022; i.e., the write bit is, by default, only set for the owner.
Note that for directories, the execute bit is set by default (allowing access to files in the directory by name).

post-factum wrote:
respiranto wrote:

The latter.  Which probably does not matter.  Currently, `/var/lib/tinc' whould be owned by `tinc', otherwise by `root'.
Again, I'd probably set `User=tinc-%i', in which case it would actually matter.  (Actually, all *Directories, except for ConfigurationDirectories would need to be distinguishable by %i).

Ack. Having the user templated is appealing, but that brings me back to DynamicUser= and all the associated mess with it. I have to think more about it.

How about a shared Group= with read access to the secret files?

Offline

#8 2021-04-05 09:25:28

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

OK, with DynamicUser and shared static group:

[Unit]
Description=Tinc net %i
Documentation=info:tinc
Documentation=man:tinc(8) man:tinc.conf(5)
Documentation=http://tinc-vpn.org/docs/
PartOf=tinc.service
ReloadPropagatedFrom=tinc.service

[Service]
DynamicUser=true
User=tinc-%i
Group=tinc
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
DeviceAllow=/dev/net/tun rw
NoNewPrivileges=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=yes
ProtectProc=invisible
ProcSubset=pid
ProtectSystem=strict
RestrictSUIDSGID=true
SystemCallArchitectures=native
RestrictRealtime=true
LockPersonality=true
MemoryDenyWriteExecute=true
RemoveIPC=true
ProtectHostname=true
SystemCallFilter=@system-service
SystemCallFilter=~@resources @privileged
WorkingDirectory=/var/lib/tinc/%i
ConfigurationDirectory=tinc/%i
ConfigurationDirectoryMode=750
RuntimeDirectory=tinc/%i
StateDirectory=tinc/%i
ExecStart=/usr/bin/tincd -D -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/tincd.pid
PIDFile=${RUNTIME_DIRECTORY}/tincd.pid
ExecReload=/usr/bin/tincd -kHUP -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/tincd.pid

[Install]
WantedBy=tinc.service

and:

g tinc

The only issue I see with this is tinc's message:

Warning: insecure file permissions for RSA private key file `/etc/tinc/test/rsa_key.priv'!

But that's because files under /etc/tinc are set to root:tinc, and tinc itself expects the file to be inaccessible to a group. I think this check is too restrictive, and if this approach is suggested to upstream, the check should be relaxed.

Also, this still allows reading the whole /etc/tinc for all tinc processes, but at least this is somewhat better than having one static user.

I haven't managed to make fully dynamic configuration work with tmpfs since it seems it is mounted after StartExecPre, hence hiding copied files underneath. Am I missing something?

Last edited by post-factum (2021-04-05 09:26:45)


uname == latest pf-kernel

Offline

#9 2021-04-05 13:48:57

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

Actually, I've just realised that the private key is a part of state, not configuration, so it really should reside under /var/lib. That's totally achievable through amending tinc config. That way they key will be owned by the appropriate user only, and the rest of the configuration can be made world-readable.


uname == latest pf-kernel

Offline

#10 2021-04-05 16:13:36

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

So, at this point:

[Unit]
Description=Tinc net %i
Documentation=info:tinc
Documentation=man:tinc(8) man:tinc.conf(5)
Documentation=http://tinc-vpn.org/docs/
PartOf=tinc.service
ReloadPropagatedFrom=tinc.service

[Service]
DynamicUser=true
User=tinc-%i
Group=tinc-%i
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
DeviceAllow=/dev/net/tun rw
NoNewPrivileges=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=yes
ProtectProc=invisible
ProcSubset=pid
ProtectSystem=strict
RestrictSUIDSGID=true
SystemCallArchitectures=native
RestrictRealtime=true
LockPersonality=true
MemoryDenyWriteExecute=true
RemoveIPC=true
ProtectHostname=true
SystemCallFilter=@system-service
SystemCallFilter=~@resources @privileged
WorkingDirectory=/var/lib/tinc/%i
ConfigurationDirectory=tinc/%i
RuntimeDirectory=tinc/%i
StateDirectory=tinc/%i
ExecStart=/usr/bin/tincd -D -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/tincd.pid
PIDFile=${RUNTIME_DIRECTORY}/tincd.pid
ExecReload=/usr/bin/tincd -kHUP -c ${CONFIGURATION_DIRECTORY} --pidfile=${RUNTIME_DIRECTORY}/tincd.pid

[Install]
WantedBy=tinc.service

This way, the tinc.conf file gains an extra field, PrivateKeyFile, which points to /var/lib/tinc/NETNAME/rsa_key.priv, which doesn't reside under /etc/tinc any more. Everything else in /etc/tinc is owned by root and is world-readable.


uname == latest pf-kernel

Offline

#11 2021-04-05 21:27:57

respiranto
Member
Registered: 2015-05-15
Posts: 479
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

`/var/lib' is for "variable state" (see hier(7)).  Does the key ever change?  (Manual change does not count, I'd say.)
Also, if the key is the same for all tincd instances, it seems awkward to me to store it in multiple locations.

Further, I'd (pedantically) replace `--pidfile=${RUNTIME_DIRECTORY}/tincd.pid' by `--pidfile=${RUNTIME_DIRECTORY}/pid'.

Offline

#12 2021-04-06 07:06:25

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

respiranto wrote:

Does the key ever change?

Not now, but we can make it change automatically ☺. I'm thinking about adding initial key provisioning to the service file (maybe, not the key itself, but just touching the file; I'm still considering how to do it) on first launch. This way it will even be easier for the user to launch tinc as the key/key template and its permissions will already be there.

respiranto wrote:

Also, if the key is the same for all tincd instances, it seems awkward to me to store it in multiple locations.

No, it is not. Every instance (network) has its own key.

respiranto wrote:

Further, I'd (pedantically) replace `--pidfile=${RUNTIME_DIRECTORY}/tincd.pid' by `--pidfile=${RUNTIME_DIRECTORY}/pid'.

Yeah, why not.


uname == latest pf-kernel

Offline

#13 2021-04-06 20:27:06

post-factum
Member
From: /cz
Registered: 2008-09-12
Posts: 149
Website

Re: [SOLVED] PKGBUILD RFC: tinc-hardened

Initiated further discussion here [1], will mark current one as solved.

Thanks for all the suggestions!

[1] https://www.tinc-vpn.org/pipermail/tinc … 00959.html


uname == latest pf-kernel

Offline

Board footer

Powered by FluxBB