mirror of
https://github.com/juanfont/headscale.git
synced 2026-03-31 14:33:42 +02:00
all: fix golangci-lint issues (#3064)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -35,6 +36,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
}
|
||||
|
||||
scenario, err := NewScenario(spec)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
@@ -46,6 +48,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
|
||||
// Create an API key using the CLI
|
||||
var validAPIKey string
|
||||
|
||||
assert.EventuallyWithT(t, func(ct *assert.CollectT) {
|
||||
apiKeyOutput, err := headscale.Execute(
|
||||
[]string{
|
||||
@@ -63,7 +66,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
|
||||
// Get the API endpoint
|
||||
endpoint := headscale.GetEndpoint()
|
||||
apiURL := fmt.Sprintf("%s/api/v1/user", endpoint)
|
||||
apiURL := endpoint + "/api/v1/user"
|
||||
|
||||
// Create HTTP client
|
||||
client := &http.Client{
|
||||
@@ -76,11 +79,12 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
t.Run("HTTP_NoAuthHeader", func(t *testing.T) {
|
||||
// Test 1: Request without any Authorization header
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
@@ -99,6 +103,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
// Should NOT contain user data after "Unauthorized"
|
||||
// This is the security bypass - if users array is present, auth was bypassed
|
||||
var jsonCheck map[string]any
|
||||
|
||||
jsonErr := json.Unmarshal(body, &jsonCheck)
|
||||
|
||||
// If we can unmarshal JSON and it contains "users", that's the bypass
|
||||
@@ -126,12 +131,13 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
t.Run("HTTP_InvalidAuthHeader", func(t *testing.T) {
|
||||
// Test 2: Request with invalid Authorization header (missing "Bearer " prefix)
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "InvalidToken")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
@@ -159,12 +165,13 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
// Test 3: Request with Bearer prefix but invalid token
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
// Note: Both malformed and properly formatted invalid tokens should return 401
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer invalid-token-12345")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
@@ -191,12 +198,13 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
t.Run("HTTP_ValidAPIKey", func(t *testing.T) {
|
||||
// Test 4: Request with valid API key
|
||||
// Expected: Should return 200 with user data (this is the authorized case)
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", validAPIKey))
|
||||
req.Header.Set("Authorization", "Bearer "+validAPIKey)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
@@ -208,16 +216,19 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
||||
|
||||
// Should be able to parse as protobuf JSON
|
||||
var response v1.ListUsersResponse
|
||||
|
||||
err = protojson.Unmarshal(body, &response)
|
||||
assert.NoError(t, err, "Response should be valid protobuf JSON with valid API key")
|
||||
require.NoError(t, err, "Response should be valid protobuf JSON with valid API key")
|
||||
|
||||
// Should contain our test users
|
||||
users := response.GetUsers()
|
||||
assert.Len(t, users, 3, "Should have 3 users")
|
||||
|
||||
userNames := make([]string, len(users))
|
||||
for i, u := range users {
|
||||
userNames[i] = u.GetName()
|
||||
}
|
||||
|
||||
assert.Contains(t, userNames, "user1")
|
||||
assert.Contains(t, userNames, "user2")
|
||||
assert.Contains(t, userNames, "user3")
|
||||
@@ -234,6 +245,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
}
|
||||
|
||||
scenario, err := NewScenario(spec)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
@@ -254,10 +266,11 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
validAPIKey := strings.TrimSpace(apiKeyOutput)
|
||||
|
||||
endpoint := headscale.GetEndpoint()
|
||||
apiURL := fmt.Sprintf("%s/api/v1/user", endpoint)
|
||||
apiURL := endpoint + "/api/v1/user"
|
||||
|
||||
t.Run("Curl_NoAuth", func(t *testing.T) {
|
||||
// Execute curl from inside the headscale container without auth
|
||||
@@ -274,17 +287,24 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
|
||||
// Parse the output
|
||||
lines := strings.Split(curlOutput, "\n")
|
||||
var httpCode string
|
||||
var responseBody string
|
||||
|
||||
var (
|
||||
httpCode string
|
||||
responseBody string
|
||||
)
|
||||
|
||||
var responseBodySb280 strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
|
||||
httpCode = after
|
||||
} else {
|
||||
responseBody += line
|
||||
responseBodySb280.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
responseBody += responseBodySb280.String()
|
||||
|
||||
// Should return 401
|
||||
assert.Equal(t, "401", httpCode,
|
||||
"Curl without auth should return 401")
|
||||
@@ -320,17 +340,24 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
lines := strings.Split(curlOutput, "\n")
|
||||
var httpCode string
|
||||
var responseBody string
|
||||
|
||||
var (
|
||||
httpCode string
|
||||
responseBody string
|
||||
)
|
||||
|
||||
var responseBodySb326 strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
|
||||
httpCode = after
|
||||
} else {
|
||||
responseBody += line
|
||||
responseBodySb326.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
responseBody += responseBodySb326.String()
|
||||
|
||||
assert.Equal(t, "401", httpCode)
|
||||
assert.Contains(t, responseBody, "Unauthorized")
|
||||
assert.NotContains(t, responseBody, "testuser1",
|
||||
@@ -346,7 +373,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
"curl",
|
||||
"-s",
|
||||
"-H",
|
||||
fmt.Sprintf("Authorization: Bearer %s", validAPIKey),
|
||||
"Authorization: Bearer " + validAPIKey,
|
||||
"-w",
|
||||
"\nHTTP_CODE:%{http_code}",
|
||||
apiURL,
|
||||
@@ -355,25 +382,34 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
lines := strings.Split(curlOutput, "\n")
|
||||
var httpCode string
|
||||
var responseBody string
|
||||
|
||||
var (
|
||||
httpCode string
|
||||
responseBody string
|
||||
)
|
||||
|
||||
var responseBodySb361 strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
|
||||
httpCode = after
|
||||
} else {
|
||||
responseBody += line
|
||||
responseBodySb361.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
responseBody += responseBodySb361.String()
|
||||
|
||||
// Should succeed
|
||||
assert.Equal(t, "200", httpCode,
|
||||
"Curl with valid API key should return 200")
|
||||
|
||||
// Should contain user data
|
||||
var response v1.ListUsersResponse
|
||||
|
||||
err = protojson.Unmarshal([]byte(responseBody), &response)
|
||||
assert.NoError(t, err, "Response should be valid protobuf JSON")
|
||||
require.NoError(t, err, "Response should be valid protobuf JSON")
|
||||
|
||||
users := response.GetUsers()
|
||||
assert.Len(t, users, 2, "Should have 2 users")
|
||||
})
|
||||
@@ -391,6 +427,7 @@ func TestGRPCAuthenticationBypass(t *testing.T) {
|
||||
}
|
||||
|
||||
scenario, err := NewScenario(spec)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
@@ -420,11 +457,12 @@ func TestGRPCAuthenticationBypass(t *testing.T) {
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
validAPIKey := strings.TrimSpace(apiKeyOutput)
|
||||
|
||||
// Get the gRPC endpoint
|
||||
// For gRPC, we need to use the hostname and port 50443
|
||||
grpcAddress := fmt.Sprintf("%s:50443", headscale.GetHostname())
|
||||
grpcAddress := headscale.GetHostname() + ":50443"
|
||||
|
||||
t.Run("gRPC_NoAPIKey", func(t *testing.T) {
|
||||
// Test 1: Try to use CLI without API key (should fail)
|
||||
@@ -452,7 +490,7 @@ func TestGRPCAuthenticationBypass(t *testing.T) {
|
||||
)
|
||||
|
||||
// Should fail with authentication error
|
||||
assert.Error(t, err,
|
||||
require.Error(t, err,
|
||||
"gRPC connection with invalid API key should fail")
|
||||
|
||||
// Should contain authentication error message
|
||||
@@ -481,20 +519,22 @@ func TestGRPCAuthenticationBypass(t *testing.T) {
|
||||
)
|
||||
|
||||
// Should succeed
|
||||
assert.NoError(t, err,
|
||||
require.NoError(t, err,
|
||||
"gRPC connection with valid API key should succeed, output: %s", output)
|
||||
|
||||
// CLI outputs the users array directly, not wrapped in ListUsersResponse
|
||||
// Parse as JSON array (CLI uses json.Marshal, not protojson)
|
||||
var users []*v1.User
|
||||
|
||||
err = json.Unmarshal([]byte(output), &users)
|
||||
assert.NoError(t, err, "Response should be valid JSON array")
|
||||
require.NoError(t, err, "Response should be valid JSON array")
|
||||
assert.Len(t, users, 2, "Should have 2 users")
|
||||
|
||||
userNames := make([]string, len(users))
|
||||
for i, u := range users {
|
||||
userNames[i] = u.GetName()
|
||||
}
|
||||
|
||||
assert.Contains(t, userNames, "grpcuser1")
|
||||
assert.Contains(t, userNames, "grpcuser2")
|
||||
})
|
||||
@@ -513,6 +553,7 @@ func TestCLIWithConfigAuthenticationBypass(t *testing.T) {
|
||||
}
|
||||
|
||||
scenario, err := NewScenario(spec)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
@@ -540,9 +581,10 @@ func TestCLIWithConfigAuthenticationBypass(t *testing.T) {
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
validAPIKey := strings.TrimSpace(apiKeyOutput)
|
||||
|
||||
grpcAddress := fmt.Sprintf("%s:50443", headscale.GetHostname())
|
||||
grpcAddress := headscale.GetHostname() + ":50443"
|
||||
|
||||
// Create a config file for testing
|
||||
configWithoutKey := fmt.Sprintf(`
|
||||
@@ -602,7 +644,7 @@ cli:
|
||||
)
|
||||
|
||||
// Should fail
|
||||
assert.Error(t, err,
|
||||
require.Error(t, err,
|
||||
"CLI with invalid API key should fail")
|
||||
|
||||
// Should indicate authentication failure
|
||||
@@ -637,20 +679,22 @@ cli:
|
||||
)
|
||||
|
||||
// Should succeed
|
||||
assert.NoError(t, err,
|
||||
require.NoError(t, err,
|
||||
"CLI with valid API key should succeed")
|
||||
|
||||
// CLI outputs the users array directly, not wrapped in ListUsersResponse
|
||||
// Parse as JSON array (CLI uses json.Marshal, not protojson)
|
||||
var users []*v1.User
|
||||
|
||||
err = json.Unmarshal([]byte(output), &users)
|
||||
assert.NoError(t, err, "Response should be valid JSON array")
|
||||
require.NoError(t, err, "Response should be valid JSON array")
|
||||
assert.Len(t, users, 2, "Should have 2 users")
|
||||
|
||||
userNames := make([]string, len(users))
|
||||
for i, u := range users {
|
||||
userNames[i] = u.GetName()
|
||||
}
|
||||
|
||||
assert.Contains(t, userNames, "cliuser1")
|
||||
assert.Contains(t, userNames, "cliuser2")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user