Skip to content

Commit 7003f1d

Browse files
authored
Merge pull request #99 from d-Rickyy-b/static-ct
Implement static ct log support
2 parents 935159e + e045980 commit 7003f1d

26 files changed

Lines changed: 1547 additions & 310 deletions

.github/goreleaser.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dockers:
4343
goarch: amd64
4444
use: buildx
4545
extra_files:
46-
- config.sample.yaml
46+
- config.docker.yaml
4747
build_flag_templates:
4848
- "--pull"
4949
- "--label=org.opencontainers.image.title={{.ProjectName}}"
@@ -62,7 +62,7 @@ dockers:
6262
goarch: arm64
6363
use: buildx
6464
extra_files:
65-
- config.sample.yaml
65+
- config.docker.yaml
6666
build_flag_templates:
6767
- "--pull"
6868
- "--label=org.opencontainers.image.title={{.ProjectName}}"

.github/workflows/changelog_reminder.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111

1212
steps:
13-
- uses: actions/checkout@v4
13+
- uses: actions/checkout@v6
1414
- name: Changelog Reminder
1515
uses: mskelton/changelog-reminder-action@v3
1616
with:

.github/workflows/release_build.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,31 @@ jobs:
2121
id: go
2222

2323
- name: Check out code into the Go module directory
24-
uses: actions/checkout@v4
24+
uses: actions/checkout@v6
2525
with:
2626
fetch-depth: 0 # See: https://goreleaser.com/ci/actions/
2727

2828
- name: Setup QEMU # Used for cross-compiling with goreleaser / docker
29-
uses: docker/setup-qemu-action@v3
29+
uses: docker/setup-qemu-action@v4
3030

3131
- name: Setup Docker Buildx # Used for cross-compiling with goreleaser / docker
32-
uses: docker/setup-buildx-action@v3
32+
uses: docker/setup-buildx-action@v4
3333

3434
- name: Login to Docker Hub
35-
uses: docker/login-action@v3
35+
uses: docker/login-action@v4
3636
with:
3737
username: ${{ secrets.DOCKERHUB_USERNAME }}
3838
password: ${{ secrets.DOCKERHUB_TOKEN }}
3939

4040
- name: Login to GitHub Container Registry
41-
uses: docker/login-action@v3
41+
uses: docker/login-action@v4
4242
with:
4343
registry: ghcr.io
4444
username: ${{ github.repository_owner }}
4545
password: ${{ secrets.GITHUB_TOKEN }}
4646

4747
- name: Run GoReleaser
48-
uses: goreleaser/goreleaser-action@v5
48+
uses: goreleaser/goreleaser-action@v7
4949
with:
5050
version: latest
5151
args: release --clean --config .github/goreleaser.yml

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Ability to store and resume processing of certs from where it left off after a restart - see sample config "recovery" (#49)
1111
- New CLI switch for creating an index file from a CT log (#49)
12+
- Support for [Static CT](https://github.com/C2SP/C2SP/blob/main/static-ct-api.md) logs
1213
- Check for retired CT logs and prevent them from being watched / stop watching them (#77)
1314
- Accept websocket connections from all origins
1415
- Option to disable the default logs provided by Google - see sample config "disable_default_logs"
16+
- Use of cobra for CLI argument parsing. New commands for displaying version and creating an index file
1517
### Changed
18+
- The configuration file for the docker container is now read from the /app/config/ directory (b9e5e6)
1619
### Removed
1720
- Non-functional Dodo log from sample config (#78)
1821
### Fixed
1922
- Properly remove stopped ct log workers (#74)
2023
- Added missing fields certificatePolicies and ctlPoisonByte (#85)
21-
- Prevent race condition caused by simultaneous rw access to logmetrics
24+
- Prevent race condition caused by simultaneous rw access to logmetrics (#91)
25+
- Properly display metrics for all initially watched logs (#95)
26+
- Properly add new metrics for all newly found logs (#96)
2227
### Docs
2328

24-
## [v1.8.2] - 2025-11-22
29+
## [1.8.2] - 2025-11-22
2530
### Fixed
2631
- Added missing fields certificatePolicies and ctlPoisonByte (#85)
2732

28-
## [v1.8.1] - 2025-05-04
33+
## [1.8.1] - 2025-05-04
2934
### Fixed
3035
- No longer reject URLs with trailing slashes defined in the `additional_logs` config (#62)
3136
- When using `drop_old_logs` in the config, the server won't remove logs defined in `additional_logs` anymore (#64)
3237

33-
## [v1.8.0] - 2025-05-03
38+
## [1.8.0] - 2025-05-03
3439
### Security
3540
- Close several CVEs in x/crypto and x/net dependencies (#59)
3641

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ RUN adduser \
1717

1818
# Copy our static executable.
1919
COPY certstream-server-go /app/certstream-server-go
20-
COPY ./config.sample.yaml /app/config.yaml
20+
COPY ./config.docker.yaml /app/config/config.yaml
21+
RUN chown -R certstreamserver:certstreamserver /app/
2122

2223
# Use an unprivileged user.
2324
USER certstreamserver:certstreamserver
2425

2526
EXPOSE 8080
2627

27-
ENTRYPOINT ["/app/certstream-server-go"]
28+
ENTRYPOINT ["/app/certstream-server-go", "-config", "/app/config/config.yaml"]

Dockerfile_multistage

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ COPY --from=builder /etc/group /etc/group
4242

4343
# Copy our static executable.
4444
COPY --from=builder /go/bin/certstream-server-go /app/certstream-server-go
45-
COPY --chown=certstreamserver:certstreamserver ./config.sample.yaml /app/config.yaml
45+
COPY --chown=certstreamserver:certstreamserver ./config.docker.yaml /app/config/config.yaml
4646

4747
# Use an unprivileged user.
4848
USER certstreamserver:certstreamserver
4949

5050
EXPOSE 8080
5151

52-
ENTRYPOINT ["/app/certstream-server-go"]
52+
ENTRYPOINT ["/app/certstream-server-go", "-config", "/app/config/config.yaml"]
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/d-Rickyy-b/certstream-server-go/internal/certstream"
12+
"github.com/d-Rickyy-b/certstream-server-go/internal/config"
13+
)
14+
15+
// createIndexCmd represents the createIndex command
16+
var createIndexCmd = &cobra.Command{
17+
Use: "create-index",
18+
Short: "Create the ct_index.json based on current STHs/Checkpoints",
19+
Long: `When using the recovery feature, certstream will store an index of the processed certificates for each CT log.
20+
create-index will create and pre fill the ct-index.json file with the current values of the most recent certificate for each CT log.`,
21+
22+
RunE: func(cmd *cobra.Command, args []string) error {
23+
configPath, err := cmd.Flags().GetString("config")
24+
if err != nil {
25+
return err
26+
}
27+
28+
conf, readConfErr := config.ReadConfig(configPath)
29+
if readConfErr != nil {
30+
return readConfErr
31+
}
32+
cs := certstream.NewRawCertstream(conf)
33+
34+
force, err := cmd.Flags().GetBool("force")
35+
if err != nil {
36+
return err
37+
}
38+
39+
outFilePath, err := cmd.Flags().GetString("out")
40+
if err != nil {
41+
return err
42+
}
43+
44+
// Check if outfile already exists
45+
outFileAbsPath, err := filepath.Abs(outFilePath)
46+
if err != nil {
47+
return err
48+
}
49+
if _, statErr := os.Stat(outFileAbsPath); statErr == nil {
50+
if !force {
51+
fmt.Printf("Output file '%s' already exists. Use --force to override it.\n", outFileAbsPath)
52+
os.Exit(1)
53+
}
54+
}
55+
56+
createErr := cs.CreateIndexFile(outFilePath)
57+
if createErr != nil {
58+
log.Fatalf("Error while creating index file: %v", createErr)
59+
}
60+
61+
return nil
62+
},
63+
}
64+
65+
func init() {
66+
rootCmd.AddCommand(createIndexCmd)
67+
68+
createIndexCmd.Flags().StringP("out", "o", "ct_index.json", "Path to the index file to create")
69+
createIndexCmd.Flags().BoolP("force", "f", false, "Whether to override the index file if it already exists")
70+
}

cmd/certstream-server-go/main.go

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,11 @@
11
package main
22

33
import (
4-
"flag"
5-
"fmt"
64
"log"
7-
8-
"github.com/d-Rickyy-b/certstream-server-go/internal/certstream"
9-
"github.com/d-Rickyy-b/certstream-server-go/internal/config"
105
)
116

127
// main is the entry point for the application.
138
func main() {
14-
configFile := flag.String("config", "config.yml", "path to the config file")
15-
versionFlag := flag.Bool("version", false, "Print the version and exit")
16-
createIndexFile := flag.Bool("create-index-file", false, "Create the ct_index.json based on current STHs")
17-
flag.Parse()
18-
19-
if *versionFlag {
20-
fmt.Printf("certstream-server-go v%s\n", config.Version)
21-
return
22-
}
23-
249
log.SetFlags(log.LstdFlags | log.Lshortfile)
25-
26-
// If the user only wants to create the index file, we don't need to start the server
27-
if *createIndexFile {
28-
conf, readConfErr := config.ReadConfig(*configFile)
29-
if readConfErr != nil {
30-
log.Fatalf("Error while reading config: %v", readConfErr)
31-
}
32-
cs := certstream.NewRawCertstream(conf)
33-
34-
createErr := cs.CreateIndexFile()
35-
if createErr != nil {
36-
log.Fatalf("Error while creating index file: %v", createErr)
37-
}
38-
39-
return
40-
}
41-
42-
log.Printf("Starting certstream-server-go v%s\n", config.Version)
43-
44-
cs, err := certstream.NewCertstreamFromConfigFile(*configFile)
45-
if err != nil {
46-
log.Fatalf("Error while creating certstream server: %v", err)
47-
}
48-
49-
cs.Start()
10+
Execute()
5011
}

cmd/certstream-server-go/root.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/d-Rickyy-b/certstream-server-go/internal/certstream"
11+
"github.com/d-Rickyy-b/certstream-server-go/internal/config"
12+
)
13+
14+
// rootCmd represents the base command when called without any subcommands
15+
var rootCmd = &cobra.Command{
16+
Use: "certstream-server-go",
17+
Short: "A drop-in replacement for the certstream server by Calidog",
18+
Long: `This tool aggregates, parses, and streams certificate data from multiple
19+
certificate transparency logs via websocket connections to connected clients.`,
20+
21+
RunE: func(cmd *cobra.Command, args []string) error {
22+
// Handle --version flag
23+
versionBool, err := cmd.Flags().GetBool("version")
24+
if err != nil {
25+
return err
26+
}
27+
if versionBool {
28+
fmt.Printf("certstream-server-go v%s\n", config.Version)
29+
return nil
30+
}
31+
32+
// Handle --config flag
33+
configPath, err := cmd.Flags().GetString("config")
34+
if err != nil {
35+
return err
36+
}
37+
// Check if path exists and is a file
38+
_, statErr := os.Stat(configPath)
39+
if os.IsNotExist(statErr) {
40+
return fmt.Errorf("config file '%s' does not exist", configPath)
41+
}
42+
43+
cs, err := certstream.NewCertstreamFromConfigFile(configPath)
44+
if err != nil {
45+
log.Fatalf("Error while creating certstream server: %v", err)
46+
}
47+
48+
cs.Start()
49+
50+
return nil
51+
},
52+
}
53+
54+
// Execute adds all child commands to the root command and sets flags appropriately.
55+
// This is called by main.main(). It only needs to happen once to the rootCmd.
56+
func Execute() {
57+
err := rootCmd.Execute()
58+
if err != nil {
59+
os.Exit(1)
60+
}
61+
}
62+
63+
func init() {
64+
rootCmd.PersistentFlags().StringP("config", "c", "config.yml", "Path to the config file")
65+
rootCmd.Flags().BoolP("version", "v", false, "Print the version and exit")
66+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/d-Rickyy-b/certstream-server-go/internal/config"
11+
)
12+
13+
// validateCmd represents the validate command
14+
var validateCmd = &cobra.Command{
15+
Use: "validate",
16+
Short: "Tests whether the config file is valid",
17+
Long: `Validates a configuration file, then exits.
18+
19+
This command deserializes the config and checks for errors.`,
20+
PreRunE: func(cmd *cobra.Command, args []string) error {
21+
// Check if config file exists
22+
configPath, err := cmd.Flags().GetString("config")
23+
if err != nil {
24+
return err
25+
}
26+
// Check if path exists and is a file
27+
_, statErr := os.Stat(configPath)
28+
if os.IsNotExist(statErr) {
29+
return fmt.Errorf("config file '%s' does not exist", configPath)
30+
}
31+
32+
return nil
33+
},
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
configPath, err := cmd.Flags().GetString("config")
36+
if err != nil {
37+
return err
38+
}
39+
40+
readConfErr := config.ValidateConfig(configPath)
41+
if readConfErr != nil {
42+
log.Fatalln(readConfErr)
43+
}
44+
log.Println("Config file is valid!")
45+
return nil
46+
},
47+
}
48+
49+
func init() {
50+
rootCmd.AddCommand(validateCmd)
51+
}

0 commit comments

Comments
 (0)