Skip to content

Commit 51869b1

Browse files
authored
Invalidate and remove join tokens after use (#207)
* Invalidate and remove join tokens after worker install is finished * Use "token invalidate", skip when "--no-wait" * Needs sudo * Add trace logging * Prototype token decoding * More prototyping * Fancy token decoder * Wrong pkg * Lint * There was a test also that was forgotten to add
1 parent 2101848 commit 51869b1

File tree

4 files changed

+108
-0
lines changed

4 files changed

+108
-0
lines changed

config/cluster/k0s.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package cluster
22

33
import (
4+
"compress/gzip"
5+
"encoding/base64"
6+
"fmt"
7+
"io"
48
"strings"
59
"time"
610

@@ -10,6 +14,7 @@ import (
1014
"github.com/k0sproject/k0sctl/integration/github"
1115
"github.com/k0sproject/k0sctl/version"
1216
"github.com/k0sproject/rig/exec"
17+
"gopkg.in/yaml.v2"
1318
)
1419

1520
// K0sMinVersion is the minimum k0s version supported
@@ -77,3 +82,51 @@ func (k K0s) GenerateToken(h *Host, role string, expiry time.Duration) (token st
7782
func (k K0s) GetClusterID(h *Host) (string, error) {
7883
return h.ExecOutput(h.Configurer.KubectlCmdf("get -n kube-system namespace kube-system -o template={{.metadata.uid}}"), exec.Sudo(h))
7984
}
85+
86+
// TokenID returns a token id from a token string that can be used to invalidate the token
87+
func TokenID(s string) (string, error) {
88+
b64 := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
89+
_, err := base64.StdEncoding.Decode(b64, []byte(s))
90+
if err != nil {
91+
return "", fmt.Errorf("failed to decode token: %w", err)
92+
}
93+
94+
sr := strings.NewReader(s)
95+
b64r := base64.NewDecoder(base64.StdEncoding, sr)
96+
gzr, err := gzip.NewReader(b64r)
97+
if err != nil {
98+
return "", fmt.Errorf("failed to create a reader for token: %w", err)
99+
}
100+
defer gzr.Close()
101+
102+
c, err := io.ReadAll(gzr)
103+
if err != nil {
104+
return "", fmt.Errorf("failed to uncompress token: %w", err)
105+
}
106+
cfg := dig.Mapping{}
107+
err = yaml.Unmarshal(c, &cfg)
108+
if err != nil {
109+
return "", fmt.Errorf("failed to unmarshal token: %w", err)
110+
}
111+
112+
users, ok := cfg.Dig("users").([]interface{})
113+
if !ok || len(users) < 1 {
114+
return "", fmt.Errorf("failed to find users in token")
115+
}
116+
117+
user, ok := users[0].(dig.Mapping)
118+
if !ok {
119+
return "", fmt.Errorf("failed to find user in token")
120+
}
121+
122+
token, ok := user.Dig("user", "token").(string)
123+
if !ok {
124+
return "", fmt.Errorf("failed to find user token in token")
125+
}
126+
127+
idx := strings.IndexRune(token, '.')
128+
if idx < 0 {
129+
return "", fmt.Errorf("failed to find separator in token")
130+
}
131+
return token[0:idx], nil
132+
}

config/cluster/k0s_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package cluster
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestTokenID(t *testing.T) {
10+
token := "H4sIAAAAAAAC/2xVXY/iOBZ9r1/BH6geO4GeAWkfKiEmGGLKjn1N/BbidAFOgjuk+Frtf18V3SPtSvN2fc/ROdaVfc9L6Q9Q9+fDqZuNLvilaj7PQ92fZy+vo9/17GU0Go3OdX+p+9loPwz+PPvjD/xn8A3/+Q19C2bfx+Pwyanqfjj8OFTlUL+Wn8P+1B+G+6sth3I2WudoWOc4FspSeYjmAqjKlaEcESWeGBpih2muRCQSNucavEEkzBWNDGoApDV1t19W6uNSbJsyRzS1mPc7TVdiDknV0qNFQmjl1zvsaZmao3RECHVd8YZEFtlEgGW8ISmXBIQiY6km+wwbr5v9yoIvVHs71pL81CAio0yYpQ2DJMFSe1InWHEZMZHQveiqa/3hf2Eg+v/FpKJdnZifHCA2aKK5IwwSsbVzYnZgJkWLdUZ8IbfCZA5CE1hSKhxliZ2rkKRxw2hxZIlSEHMgwFWCckUTi8iTmyNy+ZqJUtktO2Y9C8Wpuk8DsTUT7ehnjt9uBTQ0T7yDB9nyw+A4Tlb5wt2NbHgB5LSJpwvR2Ytpp6oKm/lG2ZvUZoDERjs9vubzamxJcZEaX6vDwLKWFeUWIoOqi7z/hWx7c2q77DfcJ5BkQQFAyxYw6xix8BZILAar8Ha3GM7l420ssZ/UZE/rrQtUytSus4ssXGKOissKkdgiOskw1fowPKRqxnFLPy0hj1pPvV6IC0t4AOhGgZDlZjFdGYdXLBVZBozKrUccW6Ra2mQNm5sF9bsHXRVqv8lB7E3XmNyZjKHTSm7Jp82HyxoJDom56HY8zgFa6/xCoOtdIL8qF8t71rDUYBZAI247ZHnpiluZn+9WNu8GsvEusFuOpvNS20J/+GUN1aN2U2kfpFQouVaBj3PsW6VgXwXVeJfSd4DlLdN2JR+gqoAed8hEBcB7OXc4J3Dl2jLuSCQCL0pHo9jhiCU2ygCcSC3hh2moFEQWNTFvfaQS2snGLJXDMdfFWCiquBKRUh8XqZZXgZIbaJEYTLbcUQnBtLDkY8VbWuzmMAhH97ka1tWWKN1lvQFLICEb3tq+0vu+VNXEPqKvN/gQjkQSsejLv3BsUjTRNk8mpNbMF46d1Ju/SURPRWihBOJtS5eVwp9ZQhvIB8+UCo1ksSXg7IPcS2wNc35cphHKVKNE4rebbSR2ODpxd5uYAA/VfH+JW9Jt1GRv231eJ9mj1uao2+Z7pRrB2ulP4+xF5kOxDtUF3PLKJXmXCb4XgQmzuRFVmmGZnCaA/nrIBdCvuRduvMpVs8lcNi7UcDVhRG0A93JLYpP66yqYgJoLoZumlQ9x2xFD8znIkux77oacdWqSdZSVyjCWnkKmb+9WDz/Nh5+b9O1SIDIUHaC6bW5V4qFsYSnSRmUIloXCuV1MaE7IsQAxBkR5ndqASRZtFDVGm7VszHGzwEfhJqzUzTV2tMi1iG369dfsmjVvkxKKfhMPgjsccEUPLMmCTcJCsTDrfGHGdXsOJcBpo4ezQd7sQroC3EQrdLtVD+Z16lZCY58rEO8SrX7vZiId/+AIckiaRa5YBIl67uU1P/3rZTTqyraejRw6v1Snbqhvw6+U+FX/Som/I+PJ+mp8np+nz13d1MPr7nQazkNf+v9X++z7uhte/1Z6Nt2hs7NRfOp+HD5efF//qPu6q+rzbPTv/7x8qT7Nf4v8g/zT+HmF4eTqbjY6fD+E949vVzeZ7vHx8mM6uPCATi//DQAA//+MVAsnAgcAAA=="
11+
12+
id, err := TokenID(token)
13+
require.NoError(t, err)
14+
require.Equal(t, "i6i3yg", id)
15+
}

phase/install_controllers.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/k0sproject/k0sctl/config"
77
"github.com/k0sproject/k0sctl/config/cluster"
8+
"github.com/k0sproject/rig/exec"
89
log "github.com/sirupsen/logrus"
910
)
1011

@@ -60,12 +61,28 @@ func (p *InstallControllers) Run() error {
6061
if err != nil {
6162
return err
6263
}
64+
tokenID, err := cluster.TokenID(token)
65+
if err != nil {
66+
return err
67+
}
68+
log.Debugf("%s: join token ID: %s", p.leader, tokenID)
69+
defer func() {
70+
if err := p.leader.Exec(p.leader.Configurer.K0sCmdf("token invalidate %s", tokenID), exec.Sudo(p.leader), exec.RedactString(token)); err != nil {
71+
log.Warnf("%s: failed to invalidate the controller join token", p.leader)
72+
}
73+
}()
6374

6475
log.Infof("%s: writing join token", h)
6576
if err := h.Configurer.WriteFile(h, h.K0sJoinTokenPath(), token, "0640"); err != nil {
6677
return err
6778
}
6879

80+
defer func() {
81+
if err := h.Configurer.DeleteFile(h, h.K0sJoinTokenPath()); err != nil {
82+
log.Warnf("%s: failed to clean up the join token file at %s", h, h.K0sJoinTokenPath())
83+
}
84+
}()
85+
6986
log.Infof("%s: installing k0s controller", h)
7087
if err := h.Exec(h.K0sInstallCommand()); err != nil {
7188
return err

phase/install_workers.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/k0sproject/k0sctl/config"
88
"github.com/k0sproject/k0sctl/config/cluster"
9+
"github.com/k0sproject/rig/exec"
910
log "github.com/sirupsen/logrus"
1011
)
1112

@@ -76,12 +77,34 @@ func (p *InstallWorkers) Run() error {
7677
return err
7778
}
7879

80+
tokenID, err := cluster.TokenID(token)
81+
if err != nil {
82+
return err
83+
}
84+
log.Debugf("%s: join token ID: %s", p.leader, tokenID)
85+
86+
if !NoWait {
87+
defer func() {
88+
if err := p.leader.Exec(p.leader.Configurer.K0sCmdf("token invalidate %s", tokenID), exec.Sudo(p.leader), exec.RedactString(token)); err != nil {
89+
log.Warnf("%s: failed to invalidate the worker join token", p.leader)
90+
}
91+
}()
92+
}
93+
7994
return p.hosts.ParallelEach(func(h *cluster.Host) error {
8095
log.Infof("%s: writing join token", h)
8196
if err := h.Configurer.WriteFile(h, h.K0sJoinTokenPath(), token, "0640"); err != nil {
8297
return err
8398
}
8499

100+
if !NoWait {
101+
defer func() {
102+
if err := h.Configurer.DeleteFile(h, h.K0sJoinTokenPath()); err != nil {
103+
log.Warnf("%s: failed to clean up the join token file at %s", h, h.K0sJoinTokenPath())
104+
}
105+
}()
106+
}
107+
85108
if sp, err := h.Configurer.ServiceScriptPath(h, h.K0sServiceName()); err == nil {
86109
if h.Configurer.ServiceIsRunning(h, h.K0sServiceName()) {
87110
log.Infof("%s: stopping service", h)

0 commit comments

Comments
 (0)