diff --git a/Dockerfile b/Dockerfile index b3e1809..57476df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,16 @@ -FROM golang:alpine AS builder +FROM golang:1.18-alpine as build -WORKDIR /workspace - -# Copy in just the go.mod and go.sum files, and download the dependencies. By -# doing this before copying in the other dependencies, the Docker build cache -# can skip these steps so long as neither of these two files change. -COPY go.mod go.sum ./ -RUN go mod download - -# Copy the rest +WORKDIR /go/src/app COPY . . -# Build the Go app with CGO_ENABLED=0 so we use the pure-Go implementations for -# things like DNS resolution (so we don't build a binary that depends on system -# libraries) -RUN CGO_ENABLED=0 go build -o /app - -# Create the 'nobody' user and group files that will be used in the running container to -# run the process an unprivileged user. -RUN mkdir /user && \ - echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \ - echo 'nobody:x:65534:' > /user/group +RUN go mod download +RUN CGO_ENABLED=0 go build -o /go/bin/app -# The final stage FROM scratch -# Copy the binary from the builder stage -COPY --from=builder /app /app - -# Copy the /etc/passwd file we created in the builder stage. This creates a new -# non-root user as a security best practice. -COPY --from=builder /user/group /user/passwd /etc/ +COPY --from=build /etc/passwd /etc/passwd +COPY --from=build /etc/group /etc/group -# Run as the new non-root by default -USER nobody:nobody +COPY --from=build /go/bin/app / -# Run the binary -ENTRYPOINT [ "/app" ] \ No newline at end of file +ENTRYPOINT ["/app"] \ No newline at end of file diff --git a/README.md b/README.md index 9eb7fe9..e534e38 100644 --- a/README.md +++ b/README.md @@ -16,26 +16,29 @@ $ RATE=10 ./nginx-log-generator The reason this is an environment variable is so it's easier to run via Docker as well: ```sh -$ docker pull kscarlett/nginx-log-generator -$ docker run -e "RATE=10" kscarlett/nginx-log-generator +$ docker pull steamvis/nginx-log-generator +$ docker run -e "RATE=10" steamvis/nginx-log-generator ``` ### Configuration The following environment variables can be set to modify the output. -| Name | Default | Notes | -| ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| RATE | 1 | Logs to output per second. | -| IPV4_PERCENT | 100 | Percentage of IP addresses that will be IPv4. Change to 0 to only get IPv6. | -| STATUS_OK_PERCENT | 80 | _Roughly_ the percentage of `200` status codes. The rest will be randomised and may contain `200` as well. | -| PATH_MIN | 1 | Minimum elements to put in the request path. | -| PATH_MAX | 5 | Maximum elements to put in the request path. | -| GET_PERCENT | 60 | Percentage of requests that will be `GET` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | -| POST_PERCENT | 30 | Percentage of requests that will be `POST` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | -| PUT_PERCENT | 0 | Percentage of requests that will be `PUT` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | -| PATCH_PERCENT | 0 | Percentage of requests that will be `PATCH` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | -| DELETE_PERCENT | 0 | Percentage of requests that will be `DELETE` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | +| Name | Default | Notes | +| ----------------- | -------- | ------------------------------------------------------------ | +| RATE | 1 | Logs to output per second. | +| IPV4_PERCENT | 100 | Percentage of IP addresses that will be IPv4. Change to 0 to only get IPv6. | +| SAME_IP_ADDRESSES | 0 | The number of identical IP addresses. If the value is 0, random IP addresses will be used | +| STDOUT | terminal | The path to the file. If the value is not equal to `terminal`, then the log is written to a file. Example `STDOUT=/var/log/nginx/access.log` | +| TRUNCATE_FILE | 0 | Truncates the file when opened. `0 `- off, `1` - on | +| STATUS_OK_PERCENT | 80 | _Roughly_ the percentage of `200` status codes. The rest will be randomised and may contain `200` as well. | +| PATH_MIN | 1 | Minimum elements to put in the request path. | +| PATH_MAX | 5 | Maximum elements to put in the request path. | +| GET_PERCENT | 60 | Percentage of requests that will be `GET` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | +| POST_PERCENT | 30 | Percentage of requests that will be `POST` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | +| PUT_PERCENT | 0 | Percentage of requests that will be `PUT` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | +| PATCH_PERCENT | 0 | Percentage of requests that will be `PATCH` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | +| DELETE_PERCENT | 0 | Percentage of requests that will be `DELETE` requests. If the total adds up to less than 100%, the rest will be made up of random HTTP methods. | ## Note diff --git a/main.go b/main.go index cb6d227..4082d38 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,9 @@ package main import ( "fmt" + "log" + "math/rand" + "os" "strings" "time" @@ -12,6 +15,9 @@ import ( type config struct { Rate float32 `env:"RATE" envDefault:"1"` IPv4Percent int `env:"IPV4_PERCENT" envDefault:"100"` + SameIpAddresses int `env:"SAME_IP_ADDRESSES" envDefault:"0"` + STDOUT string `env:"STDOUT" envDefault:"terminal"` + TruncateFile int `env:"TRUNCATE_FILE" envDefault:"0"` StatusOkPercent int `env:"STATUS_OK_PERCENT" envDefault:"80"` PathMinLength int `env:"PATH_MIN" envDefault:"1"` PathMaxLength int `env:"PATH_MAX" envDefault:"5"` @@ -40,17 +46,57 @@ func main() { httpVersion = "HTTP/1.1" referrer = "-" + sameIpAddresses := []string{} + if cfg.SameIpAddresses > 0 { + for i := 0; i <= cfg.SameIpAddresses; i++ { + sameIpAddresses = append(sameIpAddresses, weightedIPVersion(cfg.IPv4Percent)) + } + } + + var ( + fileFlags int + file *os.File + err error + ) + + if cfg.STDOUT != "terminal" { + fileFlags = os.O_WRONLY | os.O_CREATE + if cfg.TruncateFile == 1 { + fmt.Println("NGINX LOG GENERATOR [INFO]: open file with truncate") + fileFlags += os.O_TRUNC + } else { + fileFlags += os.O_APPEND + } + + file, err = os.OpenFile(cfg.STDOUT, fileFlags, 0644) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + fmt.Printf("NGINX LOG GENERATOR [INFO]: started writing to %s\n", cfg.STDOUT) + } + for range ticker.C { timeLocal = time.Now() - ip = weightedIPVersion(cfg.IPv4Percent) + if len(sameIpAddresses) == 0 { + ip = weightedIPVersion(cfg.IPv4Percent) + } else { + ip = sameIpAddresses[rand.Intn(len(sameIpAddresses))] + } httpMethod = weightedHTTPMethod(cfg.PercentageGet, cfg.PercentagePost, cfg.PercentagePut, cfg.PercentagePatch, cfg.PercentageDelete) path = randomPath(cfg.PathMinLength, cfg.PathMaxLength) statusCode = weightedStatusCode(cfg.StatusOkPercent) bodyBytesSent = realisticBytesSent(statusCode) userAgent = gofakeit.UserAgent() - fmt.Printf("%s - - [%s] \"%s %s %s\" %v %v \"%s\" \"%s\"\n", ip, timeLocal.Format("02/Jan/2006:15:04:05 -0700"), httpMethod, path, httpVersion, statusCode, bodyBytesSent, referrer, userAgent) + logLine := fmt.Sprintf("%s - - [%s] \"%s %s %s\" %v %v \"%s\" \"%s\"\n", ip, timeLocal.Format("02/Jan/2006:15:04:05 -0700"), httpMethod, path, httpVersion, statusCode, bodyBytesSent, referrer, userAgent) + if cfg.STDOUT == "terminal" { + fmt.Print(logLine) + } else { + file.WriteString(logLine) + } } }