/
authorizedkeys.go
124 lines (98 loc) · 3.4 KB
/
authorizedkeys.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright (c) 2018 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sshkeys
import (
"context"
"crypto/ed25519"
"fmt"
"os"
"gate.computer/gate/scope"
"gate.computer/gate/scope/program/system"
"gate.computer/gate/server"
"gate.computer/internal/principal"
"golang.org/x/crypto/ssh"
)
var (
errUnauthenticated = server.Unauthenticated("missing authentication credentials")
errPermissionDenied = server.PermissionDenied("key not authorized")
)
// AuthorizedKeys authorizes access for the supported (ssh-ed25519) public keys
// found in an SSH authorized_keys file.
//
// Request signatures must be verified separately by an API layer (e.g. package
// server/web).
type AuthorizedKeys struct {
server.NoAccess
server.AccessConfig
publicKeys map[[ed25519.PublicKeySize]byte]string
}
func (ak *AuthorizedKeys) ParseFile(uid, filename string) error {
text, err := os.ReadFile(filename)
if err != nil {
return err
}
return ak.Parse(uid, text)
}
func (ak *AuthorizedKeys) Parse(uid string, text []byte) error {
if ak.publicKeys == nil {
ak.publicKeys = make(map[[ed25519.PublicKeySize]byte]string)
}
for len(text) > 0 {
sshKey, comment, _, rest, err := ssh.ParseAuthorizedKey(text)
if err != nil {
return err
}
if sshKey.Type() == ssh.KeyAlgoED25519 {
cryptoKey := sshKey.(ssh.CryptoPublicKey).CryptoPublicKey()
var buf [ed25519.PublicKeySize]byte
key := cryptoKey.(ed25519.PublicKey)
if len(key) != len(buf) {
return fmt.Errorf("invalid %s public key (%s)", sshKey.Type(), comment)
}
copy(buf[:], key)
if x, exists := ak.publicKeys[buf]; exists && x != uid {
return fmt.Errorf("%s public key with multiple uids", sshKey.Type())
}
ak.publicKeys[buf] = uid
}
text = rest
}
return nil
}
func (ak *AuthorizedKeys) Authorize(ctx context.Context) (context.Context, error) {
pri := principal.ContextID(ctx)
if pri == nil {
return ctx, errUnauthenticated
}
uid, found := ak.publicKeys[principal.Raw(pri)]
if !found {
return ctx, errPermissionDenied
}
if scope.ContextContains(ctx, system.Scope) {
ctx = system.ContextWithUserID(ctx, uid)
}
return ctx, nil
}
func (ak *AuthorizedKeys) AuthorizeProgram(ctx context.Context, res *server.ResourcePolicy, prog *server.ProgramPolicy) (context.Context, error) {
ak.ConfigureResource(res)
ak.ConfigureProgram(prog)
return ak.Authorize(ctx)
}
func (ak *AuthorizedKeys) AuthorizeProgramSource(ctx context.Context, res *server.ResourcePolicy, prog *server.ProgramPolicy, _ string) (context.Context, error) {
return ak.AuthorizeProgram(ctx, res, prog)
}
func (ak *AuthorizedKeys) AuthorizeInstance(ctx context.Context, res *server.ResourcePolicy, inst *server.InstancePolicy) (context.Context, error) {
ak.ConfigureResource(res)
ak.ConfigureInstance(inst)
return ak.Authorize(ctx)
}
func (ak *AuthorizedKeys) AuthorizeProgramInstance(ctx context.Context, res *server.ResourcePolicy, prog *server.ProgramPolicy, inst *server.InstancePolicy) (context.Context, error) {
ak.ConfigureResource(res)
ak.ConfigureProgram(prog)
ak.ConfigureInstance(inst)
return ak.Authorize(ctx)
}
func (ak *AuthorizedKeys) AuthorizeProgramInstanceSource(ctx context.Context, res *server.ResourcePolicy, prog *server.ProgramPolicy, inst *server.InstancePolicy, _ string) (context.Context, error) {
return ak.AuthorizeProgramInstance(ctx, res, prog, inst)
}