Skip to content

Commit 6ec9e2b

Browse files
committed
Initial support for landlock
Fixes: #73 Signed-off-by: Morten Linderud <[email protected]>
1 parent 2d570cc commit 6ec9e2b

File tree

7 files changed

+120
-37
lines changed

7 files changed

+120
-37
lines changed

agent/agent.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,11 @@ func (a *Agent) AddKey(k *key.SSHTPMKey) error {
275275
return nil
276276
}
277277

278-
func (a *Agent) LoadKeys(keyDir string) error {
278+
func (a *Agent) LoadKeys(keys []key.SSHTPMKeys) {
279279
slog.Debug("called loadkeys")
280280
a.mu.Lock()
281281
defer a.mu.Unlock()
282-
keys, err := LoadKeys(keyDir)
283-
if err != nil {
284-
return err
285-
}
286-
287-
a.keys = keys
288-
return nil
282+
a.keys = append(a.keys, keys...)
289283
}
290284

291285
func (a *Agent) AddHierarchyKeys(hier string) error {

askpass/askpass.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func SshAskPass(prompt, hint string) ([]byte, error) {
162162
}
163163

164164
if err != nil {
165-
return []byte{}, nil
165+
return []byte{}, err
166166
}
167167
return bytes.TrimSpace(out), nil
168168
}

cmd/ssh-tpm-add/main.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import (
1111
"strings"
1212

1313
"github.com/foxboron/ssh-tpm-agent/agent"
14+
"github.com/foxboron/ssh-tpm-agent/internal/lsm"
1415
"github.com/foxboron/ssh-tpm-agent/key"
1516
"github.com/foxboron/ssh-tpm-agent/utils"
1617
"github.com/foxboron/ssh-tpm-ca-authority/client"
1718
"github.com/google/go-tpm/tpm2/transport/linuxtpm"
19+
"github.com/landlock-lsm/go-landlock/landlock"
1820
"golang.org/x/crypto/ssh"
1921
sshagent "golang.org/x/crypto/ssh/agent"
2022
)
@@ -41,9 +43,7 @@ func main() {
4143
fmt.Println(usage)
4244
}
4345

44-
var (
45-
caURL, host, user string
46-
)
46+
var caURL, host, user string
4747

4848
flag.StringVar(&caURL, "ca", "", "ca authority")
4949
flag.StringVar(&host, "host", "", "ssh hot")
@@ -56,6 +56,32 @@ func main() {
5656
os.Exit(1)
5757
}
5858

59+
lsm.RestrictAdditionalPaths(landlock.RWFiles(socket))
60+
61+
var ignorefile bool
62+
var paths []string
63+
if len(os.Args) == 1 {
64+
sshdir := utils.SSHDir()
65+
paths = []string{
66+
fmt.Sprintf("%s/id_ecdsa.tpm", sshdir),
67+
fmt.Sprintf("%s/id_rsa.tpm", sshdir),
68+
}
69+
ignorefile = true
70+
} else if len(os.Args) != 1 {
71+
paths = os.Args[1:]
72+
}
73+
74+
lsm.RestrictAdditionalPaths(
75+
// RW on socket
76+
landlock.RWFiles(socket),
77+
// RW on files we should encode/decode
78+
landlock.RWFiles(paths...),
79+
)
80+
81+
if err := lsm.Restrict(); err != nil {
82+
log.Fatal(err)
83+
}
84+
5985
conn, err := net.Dial("unix", socket)
6086
if err != nil {
6187
log.Fatal(err)
@@ -88,19 +114,6 @@ func main() {
88114
os.Exit(0)
89115
}
90116

91-
var ignorefile bool
92-
var paths []string
93-
if len(os.Args) == 1 {
94-
sshdir := utils.SSHDir()
95-
paths = []string{
96-
fmt.Sprintf("%s/id_ecdsa.tpm", sshdir),
97-
fmt.Sprintf("%s/id_rsa.tpm", sshdir),
98-
}
99-
ignorefile = true
100-
} else if len(os.Args) != 1 {
101-
paths = os.Args[1:]
102-
}
103-
104117
for _, path := range paths {
105118
b, err := os.ReadFile(path)
106119
if err != nil {

cmd/ssh-tpm-agent/main.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import (
1717
"github.com/foxboron/ssh-tpm-agent/agent"
1818
"github.com/foxboron/ssh-tpm-agent/askpass"
1919
"github.com/foxboron/ssh-tpm-agent/internal/keyring"
20+
"github.com/foxboron/ssh-tpm-agent/internal/lsm"
2021
"github.com/foxboron/ssh-tpm-agent/key"
2122
"github.com/foxboron/ssh-tpm-agent/utils"
2223
"github.com/google/go-tpm/tpm2/transport"
24+
"github.com/landlock-lsm/go-landlock/landlock"
2325
sshagent "golang.org/x/crypto/ssh/agent"
2426
"golang.org/x/term"
2527
)
@@ -180,6 +182,7 @@ func main() {
180182
var agents []sshagent.ExtendedAgent
181183

182184
for _, s := range sockets.Value {
185+
lsm.RestrictAdditionalPaths(landlock.RWFiles(s))
183186
conn, err := net.Dial("unix", s)
184187
if err != nil {
185188
slog.Error(err.Error())
@@ -188,6 +191,8 @@ func main() {
188191
agents = append(agents, sshagent.NewClient(conn))
189192
}
190193

194+
// Ensure we can rw socket path
195+
lsm.RestrictAdditionalPaths(landlock.RWFiles(socketPath))
191196
listener, err := createListener(socketPath)
192197
if err != nil {
193198
slog.Error("creating listener", slog.String("error", err.Error()))
@@ -203,6 +208,21 @@ func main() {
203208
log.Fatal(err)
204209
}
205210

211+
// We need to pre-read all the keys before we run landlock
212+
var keys []key.SSHTPMKeys
213+
if !noLoad {
214+
keys, err = agent.LoadKeys(keyDir)
215+
if err != nil {
216+
log.Fatalf("can't preload keys from ~/.ssh: %v", err)
217+
}
218+
}
219+
220+
// Try to landlock everything before we run the agent
221+
lsm.RestrictAgentFiles()
222+
if err := lsm.Restrict(); err != nil {
223+
log.Fatal(err)
224+
}
225+
206226
agent := agent.NewAgent(listener, agents,
207227

208228
// Keyring Callback
@@ -243,6 +263,7 @@ func main() {
243263
keyInfo := fmt.Sprintf("Enter passphrase for (%s): ", key.GetDescription())
244264
// TODOt kjk: askpass should box the byte slice
245265
userauth, err := askpass.ReadPassphrase(keyInfo, askpass.RP_USE_ASKPASS)
266+
fmt.Println(err)
246267
if !noCache && err == nil {
247268
slog.Debug("caching userauth for key in keyring", slog.String("fp", key.Fingerprint()))
248269
if err := agentkeyring.AddKey(key.Fingerprint(), userauth); err != nil {
@@ -266,9 +287,7 @@ func main() {
266287
}()
267288

268289
if !noLoad {
269-
if err := agent.LoadKeys(keyDir); err != nil {
270-
slog.Error("loading keys", slog.String("error", err.Error()))
271-
}
290+
agent.LoadKeys(keys)
272291
}
273292

274293
if hierarchy != "" {

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/foxboron/ssh-tpm-ca-authority v0.0.0-20240831163633-e92b30331d2d
1111
github.com/google/go-tpm v0.9.3
1212
github.com/google/go-tpm-tools v0.4.4
13+
github.com/landlock-lsm/go-landlock v0.0.0-20241014143150-479ddab4c04c
1314
github.com/rogpeppe/go-internal v1.13.1
1415
golang.org/x/crypto v0.36.0
1516
golang.org/x/sys v0.31.0
@@ -20,11 +21,12 @@ require (
2021
require (
2122
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
2223
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
23-
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
24-
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
24+
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
25+
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
2526
github.com/segmentio/ksuid v1.0.4 // indirect
2627
github.com/sigstore/sigstore v1.8.15 // indirect
2728
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
2829
golang.org/x/oauth2 v0.26.0 // indirect
2930
golang.org/x/tools v0.22.0 // indirect
31+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 // indirect
3032
)

go.sum

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDh
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8-
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250316175136-9675e468e3e2 h1:NAAT+tsS5ZgJ1n+ZP3zAPHbD9HRxGEd4xZhNoO03/So=
9-
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250316175136-9675e468e3e2/go.mod h1:uAyTlAUxchYuiFjTHmuIEJ4nGSm7iOPaGcAyA81fJ80=
108
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250318194951-cba49fbf70fa h1:2wXSGCPVpdFEi+SjcY1+SY0A0a1nbFzz/3HYuirLpX0=
119
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250318194951-cba49fbf70fa/go.mod h1:uAyTlAUxchYuiFjTHmuIEJ4nGSm7iOPaGcAyA81fJ80=
1210
github.com/foxboron/ssh-tpm-ca-authority v0.0.0-20240831163633-e92b30331d2d h1:hz0L1k0eZgHkJIgFj3Uyd0LSn7UXIwWJq9Xjj/8iGJM=
@@ -15,8 +13,8 @@ github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006 h1:50sW4r0Pcvl
1513
github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006/go.mod h1:eIXCMsMYCaqq9m1KSSxXwQG11krpuNPGP3k0uaWrbas=
1614
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
1715
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
18-
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
19-
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
16+
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
17+
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
2018
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
2119
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
2220
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
@@ -38,10 +36,12 @@ github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
3836
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
3937
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
4038
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
39+
github.com/landlock-lsm/go-landlock v0.0.0-20241014143150-479ddab4c04c h1:nwPp7v5drD5P9tUDUGF5P6Mrg7qb/oCEJBmmjlMIwG0=
40+
github.com/landlock-lsm/go-landlock v0.0.0-20241014143150-479ddab4c04c/go.mod h1:RSub3ourNF8Hf+swvw49Catm3s7HVf4hzdFxDUnEzdA=
4141
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
4242
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
43-
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
44-
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
43+
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
44+
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
4545
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
4646
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
4747
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -91,9 +91,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
9191
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
9292
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
9393
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
94+
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9495
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9596
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
96-
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9797
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9898
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9999
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -127,3 +127,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
127127
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
128128
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
129129
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
130+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI=
131+
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=

internal/lsm/lsm.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package lsm
2+
3+
import (
4+
"log/slog"
5+
"os"
6+
7+
"github.com/foxboron/ssh-tpm-agent/askpass"
8+
"github.com/landlock-lsm/go-landlock/landlock"
9+
)
10+
11+
var rules []landlock.Rule
12+
13+
func HasLandlock() bool {
14+
_, ok := os.LookupEnv("SSH_TPM_LANDLOCK")
15+
return ok
16+
}
17+
18+
func RestrictAdditionalPaths(r ...landlock.Rule) {
19+
rules = append(rules, r...)
20+
}
21+
22+
func RestrictAgentFiles() {
23+
RestrictAdditionalPaths(
24+
// Probably what we need to do for most askpass binaries
25+
landlock.RWDirs(
26+
"/usr/lib",
27+
).IgnoreIfMissing(),
28+
// Default Go paths
29+
landlock.ROFiles(
30+
"/proc/sys/net/core/somaxconn",
31+
"/etc/localtime",
32+
"/dev/null",
33+
),
34+
// We almost always want to read the TPM
35+
landlock.RWFiles(
36+
"/dev/tpm0",
37+
"/dev/tpmrm0",
38+
),
39+
// Ensure we can read+exec askpass binaries
40+
landlock.ROFiles(
41+
askpass.SSH_ASKPASS_DEFAULTS...,
42+
).IgnoreIfMissing(),
43+
)
44+
}
45+
46+
func Restrict() error {
47+
slog.Debug("sandboxing with landlock")
48+
for _, r := range rules {
49+
slog.Debug("landlock", slog.Any("rule", r))
50+
}
51+
landlock.V5.BestEffort().RestrictNet()
52+
return landlock.V5.BestEffort().RestrictPaths(rules...)
53+
}

0 commit comments

Comments
 (0)