Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build-security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
- '.github/workflows/build-security.yaml'
- '.github/workflows/reloader-dockerhub.yaml'
- 'pkg/common/**'
- 'cmd/**'
- 'platform-monitoring/operator/**'
- 'monitor-snapshot/master/operator/**'
- 'scripts/**'
- 'go.mod'
- 'go.sum'
Expand All @@ -27,6 +30,7 @@ jobs:
with:
go-version-file: go.mod
- run: go test ./pkg/common
- run: go test ./cmd/monitoring.go ./cmd/dm_rules_test.go ./cmd/persisted_rules_test.go
- run: python3 -m unittest discover -s scripts -p 'test_build_security.py' -v

reloader:
Expand Down
1 change: 1 addition & 0 deletions cmd/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ FROM busybox
# Add user and group to support run it with non-root.
ARG PINGCAP_UID=1000
ARG PINGCAP_GID=2000
ENV RELOADER_UID=${PINGCAP_UID}
RUN addgroup -g ${PINGCAP_GID} pingcap && \
adduser -u ${PINGCAP_UID} -G pingcap -h /home/pingcap -D pingcap

Expand Down
2 changes: 1 addition & 1 deletion cmd/datasource/k8s-datasource.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: 1
datasources:
- access: proxy
editable: true
editable: false
name: k8s-cluster
orgId: 1
type: prometheus
Expand Down
101 changes: 94 additions & 7 deletions cmd/dm_rules_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"io"
"net/http"
"os"
Expand All @@ -17,6 +18,83 @@ import (

type dmTransport func(*http.Request) (*http.Response, error)

func TestInitializerPreparesRuleAccess(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip("ownership preparation requires a disposable root container")
}
if runtime.GOOS == "windows" {
t.Skip("initializer requires POSIX permissions")
}
for _, source := range []string{"init.sh", "../platform-monitoring/operator/init.sh", "../monitor-snapshot/master/operator/init.sh"} {
t.Run(source, func(t *testing.T) {
script, err := os.ReadFile(source)
if err != nil {
t.Fatal(err)
}
prefix, _, ok := strings.Cut(string(script), "# Multiline replacements")
if !ok || !strings.Contains(prefix, "prepare_reloader_rules()") {
t.Fatal("initializer does not prepare reloader access")
}
dir := initializerTempDir(t)
active := filepath.Join(dir, "active rules", "rules")
data := filepath.Join(dir, "data")
store := filepath.Join(data, "latest-rules", "tidb:test")
for _, path := range []string{active, store} {
if err := os.MkdirAll(path, 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(path, "test.yml"), []byte("groups: []"), 0444); err != nil {
t.Fatal(err)
}
if err := os.Chmod(filepath.Join(path, "test.yml"), 0444); err != nil {
t.Fatal(err)
}
}
unrelated := filepath.Join(data, "prometheus")
if err := os.Mkdir(unrelated, 0700); err != nil {
t.Fatal(err)
}
run := func() error {
cmd := exec.Command("sh", "-c", prefix+"\nprepare_reloader_rules tidb:test")
cmd.Env = append(os.Environ(), "PROM_CONFIG_PATH="+filepath.Dir(active), "PROM_PERSISTENT_DIR="+data,
fmt.Sprintf("RELOADER_UID=%d", os.Geteuid()), fmt.Sprintf("RELOADER_GID=%d", os.Getegid()))
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%w: %s", err, output)
}
return nil
}
if err := run(); err != nil {
t.Fatal(err)
}
for _, path := range []string{active, store} {
info, err := os.Stat(filepath.Join(path, "test.yml"))
if err != nil || info.Mode().Perm() != 0644 {
t.Fatalf("rule permissions: %v, %v", info, err)
}
}
info, err := os.Stat(unrelated)
if err != nil || info.Mode().Perm() != 0700 {
t.Fatal("unrelated data directory permissions changed", err)
}
outside := filepath.Join(dir, "outside.yml")
if err := os.WriteFile(outside, []byte("private"), 0400); err != nil {
t.Fatal(err)
}
if err := os.Symlink(outside, filepath.Join(active, "alias.yml")); err != nil {
t.Fatal(err)
}
if err := run(); err == nil {
t.Fatal("symbolic-link rule accepted")
}
info, err = os.Stat(outside)
if err != nil || info.Mode().Perm() != 0400 {
t.Fatal("permissions changed through symlink", err)
}
})
}
}

func (f dmTransport) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }

func TestDMRulesAreGenerated(t *testing.T) {
Expand All @@ -43,7 +121,7 @@ func TestDMRulesAreGenerated(t *testing.T) {
}
oldTransport, oldAnsible, oldOperator := http.DefaultTransport, ansibleRuleDir, operatorRuleDir
t.Cleanup(func() { http.DefaultTransport, ansibleRuleDir, operatorRuleDir = oldTransport, oldAnsible, oldOperator })
ansibleRuleDir, operatorRuleDir = t.TempDir(), t.TempDir()
ansibleRuleDir, operatorRuleDir = initializerTempDir(t), initializerTempDir(t)
http.DefaultTransport = dmTransport(func(*http.Request) (*http.Response, error) {
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(string(body)))}, nil
})
Expand All @@ -67,7 +145,7 @@ func TestDMInitializerRejectsMissingRules(t *testing.T) {
}
for _, source := range []string{"init.sh", "../platform-monitoring/operator/init.sh", "../monitor-snapshot/master/operator/init.sh"} {
t.Run(source, func(t *testing.T) {
root := t.TempDir()
root := initializerTempDir(t)
assets := filepath.Join(root, "assets")
extraPath := ""
if runtime.GOOS == "darwin" {
Expand Down Expand Up @@ -120,7 +198,7 @@ func TestDMInitializerRejectsMissingRules(t *testing.T) {
}
}
cmd := exec.Command("sh", scriptPath)
cmd.Env = append(os.Environ(), "DM_CLUSTER_NAME=test", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana space"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus space"), "PROM_PERSISTENT_DIR=", "GF_DATASOURCE_PATH="+datasource, "GF_DM_PROMETHEUS_URL=http://example.test/?a=1&b=2", "PATH="+extraPath+os.Getenv("PATH"))
cmd.Env = append(os.Environ(), fmt.Sprintf("RELOADER_UID=%d", os.Geteuid()), fmt.Sprintf("RELOADER_GID=%d", os.Getegid()), "DM_CLUSTER_NAME=test", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana space"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus space"), "PROM_PERSISTENT_DIR=", "GF_DATASOURCE_PATH="+datasource, "GF_DM_PROMETHEUS_URL=http://example.test/?a=1&b=2", "PATH="+extraPath+os.Getenv("PATH"))
out, err := cmd.CombinedOutput()
if !hasRules && err == nil {
t.Errorf("missing rules reported success: %s", out)
Expand All @@ -146,9 +224,9 @@ func TestInitializerRejectsMultilineReplacement(t *testing.T) {
}
for _, source := range []string{"init.sh", "../platform-monitoring/operator/init.sh", "../monitor-snapshot/master/operator/init.sh"} {
t.Run(source, func(t *testing.T) {
root := t.TempDir()
root := initializerTempDir(t)
cmd := exec.Command("sh", source)
cmd.Env = append(os.Environ(), "DM_CLUSTER_NAME=test", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus"), "GF_DM_PROMETHEUS_URL=bad\nreplacement")
cmd.Env = append(os.Environ(), fmt.Sprintf("RELOADER_UID=%d", os.Geteuid()), fmt.Sprintf("RELOADER_GID=%d", os.Getegid()), "DM_CLUSTER_NAME=test", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus"), "GF_DM_PROMETHEUS_URL=bad\nreplacement")
out, err := cmd.CombinedOutput()
if err == nil || !strings.Contains(string(out), "must be single-line") {
t.Fatalf("multiline value not rejected: %v %s", err, out)
Expand All @@ -167,7 +245,7 @@ func TestTiDBInitializerPreservesDatasourceURLs(t *testing.T) {
}
for _, source := range []string{"init.sh", "../platform-monitoring/operator/init.sh", "../monitor-snapshot/master/operator/init.sh"} {
t.Run(source, func(t *testing.T) {
root := t.TempDir()
root := initializerTempDir(t)
assets := filepath.Join(root, "assets")
datasource := filepath.Join(root, "data sources")
for _, dir := range []string{assets, datasource} {
Expand Down Expand Up @@ -224,7 +302,7 @@ func TestTiDBInitializerPreservesDatasourceURLs(t *testing.T) {
cmd.Dir = root
k8sURL := "http://example.test/?a=1&b=2,tail"
tidbURL := "http://example.test/tidb?a=1&b=2"
cmd.Env = append(os.Environ(), "DM_CLUSTER_NAME=", "TIDB_CLUSTER_NAME=test", "TIDB_CLUSTER_NAMESPACE=default", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana space"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus space"), "PROM_PERSISTENT_DIR=", "GF_DATASOURCE_PATH="+datasource, "GF_K8S_PROMETHEUS_URL="+k8sURL, "GF_TIDB_PROMETHEUS_URL="+tidbURL, "PATH="+extraPath+os.Getenv("PATH"))
cmd.Env = append(os.Environ(), fmt.Sprintf("RELOADER_UID=%d", os.Geteuid()), fmt.Sprintf("RELOADER_GID=%d", os.Getegid()), "DM_CLUSTER_NAME=", "TIDB_CLUSTER_NAME=test", "TIDB_CLUSTER_NAMESPACE=default", "GF_PROVISIONING_PATH="+filepath.Join(root, "grafana space"), "PROM_CONFIG_PATH="+filepath.Join(root, "prometheus space"), "PROM_PERSISTENT_DIR=", "GF_DATASOURCE_PATH="+datasource, "GF_K8S_PROMETHEUS_URL="+k8sURL, "GF_TIDB_PROMETHEUS_URL="+tidbURL, "PATH="+extraPath+os.Getenv("PATH"))
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("TiDB initializer failed: %v %s", err, out)
}
Expand All @@ -237,3 +315,12 @@ func TestTiDBInitializerPreservesDatasourceURLs(t *testing.T) {
})
}
}

func initializerTempDir(t *testing.T) string {
t.Helper()
dir, err := filepath.EvalSymlinks(t.TempDir())
if err != nil {
t.Fatal(err)
}
return dir
}
Loading
Loading