Skip to content

Commit f4842ca

Browse files
harshavardhanaminio-trusted
authored andcommitted
rewrite logging in console
- enhance logging throughout the codebase - all packages at pkg/ should never log or perform log.Fatal() instead packages should return errors through functions. - simplified various user, group mapping and removed redundant functions. - deprecate older flags like --tls-certificate --tls-key and --tls-ca as we do not use them anymore, keep them for backward compatibility for some time.
1 parent 83d6620 commit f4842ca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+451
-499
lines changed

cmd/console/server.go

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"io/ioutil"
23-
"log"
24-
"os"
2523
"path/filepath"
26-
"strconv"
2724
"time"
2825

2926
"github.com/go-openapi/loads"
@@ -38,18 +35,18 @@ import (
3835
var serverCmd = cli.Command{
3936
Name: "server",
4037
Aliases: []string{"srv"},
41-
Usage: "starts Console server",
38+
Usage: "Start MinIO Console server",
4239
Action: StartServer,
4340
Flags: []cli.Flag{
4441
cli.StringFlag{
4542
Name: "host",
4643
Value: restapi.GetHostname(),
47-
Usage: "hostname",
44+
Usage: "bind to a specific HOST, HOST can be an IP or hostname",
4845
},
4946
cli.IntFlag{
5047
Name: "port",
5148
Value: restapi.GetPort(),
52-
Usage: "HTTP port",
49+
Usage: "bind to specific HTTP port",
5350
},
5451
// This is kept here for backward compatibility,
5552
// hostname's do not have HTTP or HTTPs
@@ -58,52 +55,52 @@ var serverCmd = cli.Command{
5855
cli.StringFlag{
5956
Name: "tls-host",
6057
Value: restapi.GetHostname(),
61-
Usage: "HTTPS hostname",
6258
Hidden: true,
6359
},
60+
cli.StringFlag{
61+
Name: "certs-dir",
62+
Value: certs.GlobalCertsCADir.Get(),
63+
Usage: "path to certs directory",
64+
},
6465
cli.IntFlag{
6566
Name: "tls-port",
6667
Value: restapi.GetTLSPort(),
67-
Usage: "HTTPS port",
68+
Usage: "bind to specific HTTPS port",
6869
},
6970
cli.StringFlag{
7071
Name: "tls-redirect",
7172
Value: restapi.GetTLSRedirect(),
7273
Usage: "toggle HTTP->HTTPS redirect",
7374
},
7475
cli.StringFlag{
75-
Name: "certs-dir",
76-
Value: certs.GlobalCertsCADir.Get(),
77-
Usage: "path to certs directory",
78-
},
79-
cli.StringFlag{
80-
Name: "tls-certificate",
81-
Value: "",
82-
Usage: "path to TLS public certificate",
76+
Name: "tls-certificate",
77+
Value: "",
78+
Usage: "path to TLS public certificate",
79+
Hidden: true,
8380
},
8481
cli.StringFlag{
85-
Name: "tls-key",
86-
Value: "",
87-
Usage: "path to TLS private key",
82+
Name: "tls-key",
83+
Value: "",
84+
Usage: "path to TLS private key",
85+
Hidden: true,
8886
},
8987
cli.StringFlag{
90-
Name: "tls-ca",
91-
Value: "",
92-
Usage: "path to TLS Certificate Authority",
88+
Name: "tls-ca",
89+
Value: "",
90+
Usage: "path to TLS Certificate Authority",
91+
Hidden: true,
9392
},
9493
},
9594
}
9695

97-
// StartServer starts the console service
98-
func StartServer(ctx *cli.Context) error {
96+
func buildServer() (*restapi.Server, error) {
9997
swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
10098
if err != nil {
101-
log.Fatalln(err)
99+
return nil, err
102100
}
103101

104102
api := operations.NewConsoleAPI(swaggerSpec)
105103
server := restapi.NewServer(api)
106-
defer server.Shutdown()
107104

108105
parser := flags.NewParser(server, flags.Default)
109106
parser.ShortDescription = "MinIO Console Server"
@@ -114,33 +111,31 @@ func StartServer(ctx *cli.Context) error {
114111
for _, optsGroup := range api.CommandLineOptionsGroups {
115112
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
116113
if err != nil {
117-
log.Fatalln(err)
114+
return nil, err
118115
}
119116
}
120117

121118
if _, err := parser.Parse(); err != nil {
122-
code := 1
123-
if fe, ok := err.(*flags.Error); ok {
124-
if fe.Type == flags.ErrHelp {
125-
code = 0
126-
}
127-
}
128-
os.Exit(code)
119+
return nil, err
129120
}
130121

131-
server.Host = ctx.String("host")
132-
server.Port = ctx.Int("port")
133-
restapi.Hostname = server.Host
134-
restapi.Port = strconv.Itoa(server.Port)
122+
return server, nil
123+
}
135124

125+
func loadAllCerts(ctx *cli.Context) error {
126+
var err error
136127
// Set all certs and CAs directories path
137-
certs.GlobalCertsDir, _ = certs.NewConfigDirFromCtx(ctx, "certs-dir", certs.DefaultCertsDir.Get)
138-
certs.GlobalCertsCADir = &certs.ConfigDir{Path: filepath.Join(certs.GlobalCertsDir.Get(), certs.CertsCADir)}
128+
certs.GlobalCertsDir, _, err = certs.NewConfigDirFromCtx(ctx, "certs-dir", certs.DefaultCertsDir.Get)
129+
if err != nil {
130+
return err
131+
}
139132

133+
certs.GlobalCertsCADir = &certs.ConfigDir{Path: filepath.Join(certs.GlobalCertsDir.Get(), certs.CertsCADir)}
140134
// check if certs and CAs directories exists or can be created
141-
if err := certs.MkdirAllIgnorePerm(certs.GlobalCertsCADir.Get()); err != nil {
142-
log.Println(fmt.Sprintf("Unable to create certs CA directory at %s", certs.GlobalCertsCADir.Get()))
135+
if err = certs.MkdirAllIgnorePerm(certs.GlobalCertsCADir.Get()); err != nil {
136+
return fmt.Errorf("Unable to create certs CA directory at %s: with %w", certs.GlobalCertsCADir.Get(), err)
143137
}
138+
144139
// load the certificates and the CAs
145140
restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager = certs.GetAllCertificatesAndCAs()
146141

@@ -153,7 +148,7 @@ func StartServer(ctx *cli.Context) error {
153148
if swaggerServerCertificate != "" && swaggerServerCertificateKey != "" {
154149
if err = certs.AddCertificate(context.Background(),
155150
restapi.GlobalTLSCertsManager, swaggerServerCertificate, swaggerServerCertificateKey); err != nil {
156-
log.Fatalln(err)
151+
return err
157152
}
158153
if x509Certs, err := certs.ParsePublicCertFile(swaggerServerCertificate); err == nil {
159154
restapi.GlobalPublicCerts = append(restapi.GlobalPublicCerts, x509Certs...)
@@ -169,25 +164,40 @@ func StartServer(ctx *cli.Context) error {
169164
}
170165
}
171166

172-
if len(restapi.GlobalPublicCerts) > 0 {
173-
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
174-
// plain HTTP connections to HTTPS server
175-
server.EnabledListeners = []string{"http", "https"}
176-
server.TLSPort = ctx.Int("tls-port")
177-
// Need to store tls-port, tls-host un config variables so secure.middleware can read from there
178-
restapi.TLSPort = strconv.Itoa(server.TLSPort)
179-
restapi.Hostname = ctx.String("host")
180-
restapi.TLSRedirect = ctx.String("tls-redirect")
167+
return nil
168+
}
169+
170+
// StartServer starts the console service
171+
func StartServer(ctx *cli.Context) error {
172+
if err := loadAllCerts(ctx); err != nil {
173+
restapi.LogError("Unable to load certs: %v", err)
174+
return err
175+
}
176+
177+
var rctx restapi.Context
178+
if err := rctx.Load(ctx); err != nil {
179+
restapi.LogError("argument validation failed: %v", err)
180+
return err
181181
}
182182

183-
server.ConfigureAPI()
183+
server, err := buildServer()
184+
if err != nil {
185+
restapi.LogError("Unable to initialize console server: %v", err)
186+
return err
187+
}
188+
189+
s := server.Configure(rctx)
190+
defer s.Shutdown()
184191

185192
// subnet license refresh process
186193
go func() {
194+
// start refreshing subnet license after 5 seconds..
195+
time.Sleep(time.Second * 5)
196+
187197
failedAttempts := 0
188198
for {
189199
if err := restapi.RefreshLicense(); err != nil {
190-
log.Println(err)
200+
restapi.LogError("Refreshing subnet license failed: %v", err)
191201
failedAttempts++
192202
// end license refresh after 3 consecutive failed attempts
193203
if failedAttempts >= 3 {
@@ -204,8 +214,5 @@ func StartServer(ctx *cli.Context) error {
204214
}
205215
}()
206216

207-
if err := server.Serve(); err != nil {
208-
log.Fatalln(err)
209-
}
210-
return nil
217+
return s.Serve()
211218
}

pkg/auth/idp/oauth2/provider.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"encoding/base64"
2323
"errors"
2424
"fmt"
25-
"log"
2625
"net/http"
2726
"net/url"
2827
"strconv"
@@ -37,10 +36,6 @@ import (
3736
xoauth2 "golang.org/x/oauth2"
3837
)
3938

40-
var (
41-
errGeneric = errors.New("an error occurred, please try again")
42-
)
43-
4439
type Configuration interface {
4540
Exchange(ctx context.Context, code string, opts ...xoauth2.AuthCodeOption) (*xoauth2.Token, error)
4641
AuthCodeURL(state string, opts ...xoauth2.AuthCodeOption) string
@@ -168,8 +163,8 @@ type User struct {
168163
// VerifyIdentity will contact the configured IDP and validate the user identity based on the authorization code
169164
func (client *Provider) VerifyIdentity(ctx context.Context, code, state string) (*credentials.Credentials, error) {
170165
// verify the provided state is valid (prevents CSRF attacks)
171-
if !validateOauth2State(state) {
172-
return nil, errGeneric
166+
if err := validateOauth2State(state); err != nil {
167+
return nil, err
173168
}
174169
getWebTokenExpiry := func() (*credentials.WebIdentityToken, error) {
175170
oauth2Token, err := client.oauth2Config.Exchange(ctx, code)
@@ -210,29 +205,30 @@ func (client *Provider) VerifyIdentity(ctx context.Context, code, state string)
210205
// validateOauth2State validates the provided state was originated using the same
211206
// instance (or one configured using the same secrets) of Console, this is basically used to prevent CSRF attacks
212207
// https://security.stackexchange.com/questions/20187/oauth2-cross-site-request-forgery-and-state-parameter
213-
func validateOauth2State(state string) bool {
208+
func validateOauth2State(state string) error {
214209
// state contains a base64 encoded string that may ends with "==", the browser encodes that to "%3D%3D"
215210
// query unescape is need it before trying to decode the base64 string
216211
encodedMessage, err := url.QueryUnescape(state)
217212
if err != nil {
218-
log.Println(err)
219-
return false
213+
return err
220214
}
221215
// decode the state parameter value
222216
message, err := base64.StdEncoding.DecodeString(encodedMessage)
223217
if err != nil {
224-
log.Println(err)
225-
return false
218+
return err
226219
}
227220
s := strings.Split(string(message), ":")
228221
// Validate that the decoded message has the right format "message:hmac"
229222
if len(s) != 2 {
230-
return false
223+
return fmt.Errorf("invalid number of tokens, expected only 2, got %d instead", len(s))
231224
}
232225
// extract the state and hmac
233226
incomingState, incomingHmac := s[0], s[1]
234227
// validate that hmac(incomingState + pbkdf2(secret, salt)) == incomingHmac
235-
return utils.ComputeHmac256(incomingState, derivedKey) == incomingHmac
228+
if calculatedHmac := utils.ComputeHmac256(incomingState, derivedKey); calculatedHmac != incomingHmac {
229+
return fmt.Errorf("oauth2 state is invalid, expected %d, got %d", calculatedHmac, incomingHmac)
230+
}
231+
return nil
236232
}
237233

238234
// GetRandomStateWithHMAC computes message + hmac(message, pbkdf2(key, salt)) to be used as state during the oauth authorization

pkg/auth/operator.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package auth
1818

1919
import (
2020
"context"
21-
"log"
2221

2322
"github.com/minio/console/cluster"
2423
"github.com/minio/minio-go/v7/pkg/credentials"
@@ -64,16 +63,12 @@ func (c *operatorClient) Authenticate(ctx context.Context) ([]byte, error) {
6463
return c.client.RESTClient().Verb("GET").RequestURI("/api").DoRaw(ctx)
6564
}
6665

67-
// isServiceAccountTokenValid will make an authenticated request against kubernetes api, if the
66+
// checkServiceAccountTokenValid will make an authenticated request against kubernetes api, if the
6867
// request success means the provided jwt its a valid service account token and the console user can use it for future
6968
// requests until it expires
70-
func isServiceAccountTokenValid(ctx context.Context, operatorClient OperatorClient) bool {
69+
func checkServiceAccountTokenValid(ctx context.Context, operatorClient OperatorClient) error {
7170
_, err := operatorClient.Authenticate(ctx)
72-
if err != nil {
73-
log.Println(err)
74-
return false
75-
}
76-
return true
71+
return err
7772
}
7873

7974
// GetConsoleCredentialsForOperator will validate the provided JWT (service account token) and return it in the form of credentials.Login
@@ -86,8 +81,8 @@ func GetConsoleCredentialsForOperator(jwt string) (*credentials.Credentials, err
8681
opClient := &operatorClient{
8782
client: opClientClientSet,
8883
}
89-
if isServiceAccountTokenValid(ctx, opClient) {
90-
return credentials.New(operatorCredentialsProvider{serviceAccountJWT: jwt}), nil
84+
if err = checkServiceAccountTokenValid(ctx, opClient); err != nil {
85+
return nil, errInvalidCredentials
9186
}
92-
return nil, errInvalidCredentials
87+
return credentials.New(operatorCredentialsProvider{serviceAccountJWT: jwt}), nil
9388
}

pkg/auth/operator_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ func (c *operatorClientTest) Authenticate(ctx context.Context) ([]byte, error) {
2020
return operatorAuthenticateMock(ctx)
2121
}
2222

23-
func Test_isServiceAccountTokenValid(t *testing.T) {
24-
23+
func Test_checkServiceAccountTokenValid(t *testing.T) {
2524
successResponse := func() {
2625
operatorAuthenticateMock = func(ctx context.Context) ([]byte, error) {
2726
return nil, nil
@@ -70,12 +69,17 @@ func Test_isServiceAccountTokenValid(t *testing.T) {
7069
},
7170
}
7271
for _, tt := range tests {
72+
tt := tt
7373
t.Run(tt.name, func(t *testing.T) {
7474
if tt.args.mockFunction != nil {
7575
tt.args.mockFunction()
7676
}
77-
if got := isServiceAccountTokenValid(tt.args.ctx, tt.args.operatorClient); got != tt.want {
78-
t.Errorf("isServiceAccountTokenValid() = %v, want %v", got, tt.want)
77+
got := checkServiceAccountTokenValid(tt.args.ctx, tt.args.operatorClient)
78+
if got != nil && tt.want {
79+
t.Errorf("checkServiceAccountTokenValid() = expected success but got %s", got)
80+
}
81+
if got == nil && !tt.want {
82+
t.Error("checkServiceAccountTokenValid() = expected failure but got success")
7983
}
8084
})
8185
}

0 commit comments

Comments
 (0)