diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 5ff7f19..5b41dbe 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,11 +8,11 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.23.1' + GO_VERSION: '1.24.2' jobs: build-common: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Main Repo @@ -40,8 +40,10 @@ jobs: name: CHANGELOG.md path: ./CHANGELOG.md +### + build-linux-arm64: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -50,12 +52,6 @@ jobs: ref: ${{ env.GITHUB_REF }} fetch-depth: 0 - - name: Update apt - run: sudo apt update - - - name: Install cross-compiler for linux/arm64 - run: sudo apt-get -y install gcc-aarch64-linux-gnu - - name: Set up Go uses: actions/setup-go@v5 with: @@ -66,7 +62,6 @@ jobs: env: GOOS: linux GOARCH: arm64 - CC: aarch64-linux-gnu-gcc CGO_ENABLED: 0 - name: Save Compiled Binary @@ -90,7 +85,7 @@ jobs: path: ./apc-p15-install build-linux-amd64: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Backend Repo uses: actions/checkout@v4 @@ -171,9 +166,93 @@ jobs: name: apc-p15-install-windows-amd64 path: ./apc-p15-install.exe + build-darwin-arm64: + runs-on: macos-15 + steps: + - name: Checkout Backend Repo + uses: actions/checkout@v4 + with: + repository: gregtwallace/apc-p15-tool + ref: ${{ env.GITHUB_REF }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ env.GO_VERSION }}' + + - name: Build Tool + run: go build -o ./apc-p15-tool -v ./cmd/tool + env: + GOOS: darwin + GOARCH: arm64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool-darwin-arm64 + path: ./apc-p15-tool + + - name: Build Install Only + run: go build -o ./apc-p15-install -v ./cmd/install_only + env: + GOOS: darwin + GOARCH: arm64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-install-darwin-arm64 + path: ./apc-p15-install + + build-darwin-amd64: + runs-on: macos-13 + steps: + - name: Checkout Backend Repo + uses: actions/checkout@v4 + with: + repository: gregtwallace/apc-p15-tool + ref: ${{ env.GITHUB_REF }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ env.GO_VERSION }}' + + - name: Build Tool + run: go build -o ./apc-p15-tool -v ./cmd/tool + env: + GOOS: darwin + GOARCH: amd64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool-darwin-amd64 + path: ./apc-p15-tool + + - name: Build Install Only + run: go build -o ./apc-p15-install -v ./cmd/install_only + env: + GOOS: darwin + GOARCH: amd64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-install-darwin-amd64 + path: ./apc-p15-install + +### + release-file-linux-arm64: needs: [build-common, build-linux-arm64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory @@ -217,7 +296,7 @@ jobs: release-file-linux-amd64: needs: [build-common, build-linux-amd64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory @@ -261,7 +340,7 @@ jobs: release-file-windows-amd64: needs: [build-common, build-windows-amd64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory @@ -302,3 +381,91 @@ jobs: with: name: apc-p15-tool_windows_amd64 path: ./release + + release-file-darwin-arm64: + needs: [build-common, build-darwin-arm64] + runs-on: ubuntu-24.04 + + steps: + - name: Make release directory + run: mkdir ./release + + - name: Download Tool Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-tool-darwin-arm64 + path: ./release + + - name: Download Install Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-install-darwin-arm64 + path: ./release + + - name: Download README + uses: actions/download-artifact@v4 + with: + name: README.md + path: ./release + + - name: Download LICENSE + uses: actions/download-artifact@v4 + with: + name: LICENSE.md + path: ./release + + - name: Download CHANGELOG + uses: actions/download-artifact@v4 + with: + name: CHANGELOG.md + path: ./release + + - name: Save Release + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool_darwin_arm64 + path: ./release + + release-file-darwin-amd64: + needs: [build-common, build-darwin-amd64] + runs-on: ubuntu-24.04 + + steps: + - name: Make release directory + run: mkdir ./release + + - name: Download Tool Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-tool-darwin-amd64 + path: ./release + + - name: Download Install Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-install-darwin-amd64 + path: ./release + + - name: Download README + uses: actions/download-artifact@v4 + with: + name: README.md + path: ./release + + - name: Download LICENSE + uses: actions/download-artifact@v4 + with: + name: LICENSE.md + path: ./release + + - name: Download CHANGELOG + uses: actions/download-artifact@v4 + with: + name: CHANGELOG.md + path: ./release + + - name: Save Release + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool_darwin_amd64 + path: ./release diff --git a/CHANGELOG.md b/CHANGELOG.md index 4936f32..cc534a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # APC P15 Tool Changelog +## [v1.2.2] - 2025-04-22 + +All dependencies updated. + +Add darwin arm64 and amd64 builds. + + +## [v1.2.1] - 2025-03-17 + +Fix time check for UPS when it is set to GMT timezone. + +All dependencies updated. + + +## [v1.2.0] - 2025-01-27 + +Add a new feature to `install` that checks the time of the UPS to confirm +it is accurate. A log message is added that advises either way. Even if +the check fails, the install still proceeds with attempting to install +the new certificate. + +Dependencies were also all updated. + + ## [v1.1.0] - 2024-09-17 > [!IMPORTANT] diff --git a/README.md b/README.md index d339961..8a9218f 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,10 @@ NMC3*: - RSA 1,024, 2,048, 3,072, and 4,092 bit lengths. - ECDSA curves P-256, P-384, and P-521. -* 3,072 bit length is not officially supported by my NMC2, but appears to work +\* 3,072 bit length is not officially supported by my NMC2, but appears to work fine. -* The additional key types supported by NMC3 require newer firmware on the + +\* The additional key types supported by NMC3 require newer firmware on the device. I am unsure what the version cutoff is, but you can check support by connecting to the UPS via SSH and typing `ssl`. If `Command Not Found` is returned, the firmware is too old and only the key types listed under @@ -170,6 +171,11 @@ separate script.  +## Links + +@Owl-Tec's write up using this tool with ACDS: +https://owltec.ca/Windows+Server/Deploying+An+Internal+HTTPS+Certificate+for+a+UPS+APC+with+ADCS+(Active+Directory+Certificate+Services)+with+APC+P15+Tool + ## Thanks Special thanks to the following people and resources which helped me diff --git a/build.ps1 b/build.ps1 index 7147a33..0c8774b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -34,3 +34,25 @@ $env:GOARCH = "arm64" $env:GOOS = "linux" $env:CGO_ENABLED = 0 go build -o $outDir/apc-p15-install-arm64 ./cmd/install_only + +# Darwin (MacOS) amd64 +$env:GOARCH = "amd64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-tool-darwin-amd64 ./cmd/tool + +$env:GOARCH = "amd64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-install-darwin-amd64 ./cmd/install_only + +# Darwin (MacOS) arm64 +$env:GOARCH = "arm64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-tool-darwin-arm64 ./cmd/tool + +$env:GOARCH = "arm64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-install-darwin-arm64 ./cmd/install_only diff --git a/go.mod b/go.mod index 11730be..76eb3ca 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module apc-p15-tool -go 1.23.1 +go 1.24.2 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 - github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 - golang.org/x/crypto v0.18.0 + github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 + golang.org/x/crypto v0.37.0 ) -require golang.org/x/sys v0.16.0 // indirect +require golang.org/x/sys v0.32.0 // indirect replace apc-p15-tool/cmd/install_only => /cmd/install_only diff --git a/go.sum b/go.sum index 96676f0..0b94b0b 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,13 @@ github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= -github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= -github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I= +github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/img/apc-p15-tool.png b/img/apc-p15-tool.png index 807fb84..c537585 100644 Binary files a/img/apc-p15-tool.png and b/img/apc-p15-tool.png differ diff --git a/pkg/apcssh/cmd_gettime.go b/pkg/apcssh/cmd_gettime.go new file mode 100644 index 0000000..139b0ba --- /dev/null +++ b/pkg/apcssh/cmd_gettime.go @@ -0,0 +1,62 @@ +package apcssh + +import ( + "fmt" + "regexp" + "strings" + "time" +) + +// GetTime sends the APC `system` command and then attempts to parse the +// response to determine the UPS current date/time. +func (cli *Client) GetTime() (time.Time, error) { + result, err := cli.cmd("date") + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s)", err) + } else if !strings.EqualFold(result.code, "e000") { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s: %s)", result.code, result.codeText) + } + + // capture each portion of the date information + regex := regexp.MustCompile(`Date:\s*(\S*)\s*[\r\n]Time:\s*(\S*)\s*[\r\n]Format:\s*(\S*)\s*[\r\n]Time Zone:\s*(\S*)\s*[\r\n]?`) + datePieces := regex.FindStringSubmatch(result.resultText) + if len(datePieces) != 5 { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (length of datetime value pieces was %d (expected: 5))", len(datePieces)) + } + dateVal := datePieces[1] + timeVal := datePieces[2] + formatUPSVal := datePieces[3] + timeZoneVal := datePieces[4] + + // GMT time requires + prefix + // APC UPS fails to use the required +, so add it + if timeZoneVal == "00:00" { + timeZoneVal = "+" + timeZoneVal + } + + // known APC UPS format strings + dateFormatVal := "" + switch formatUPSVal { + case "mm/dd/yyyy": + dateFormatVal = "01/02/2006" + case "dd.mm.yyyy": + dateFormatVal = "02.01.2006" + case "mmm-dd-yy": + dateFormatVal = "Jan-02-06" + case "dd-mmm-yy": + dateFormatVal = "02-Jan-06" + case "yyyy-mm-dd": + dateFormatVal = "2006-01-02" + + default: + return time.Time{}, fmt.Errorf("apcssh: failed to get time (ups returned unknown format string (%s)", formatUPSVal) + } + + // convert to time.Time + t, err := time.Parse(dateFormatVal+" 15:04:05 -07:00", dateVal+" "+timeVal+" "+timeZoneVal) + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (time parse failed: %s)", err) + } + + return t, nil +} diff --git a/pkg/app/app.go b/pkg/app/app.go index 9a3f542..052a00e 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "1.1.0" + appVersion = "1.2.2" ) // struct for receivers to use common app pieces diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index a431eb8..eacda53 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -12,6 +12,8 @@ import ( "time" ) +const timeLoggingFormat = time.RFC1123Z + // cmdInstall is the app's command to create apc p15 file content from key and cert // pem files and upload the p15 to the specified APC UPS func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { @@ -75,6 +77,16 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...") + // check time - don't fail it time is no good, just do logging here + upsT, err := client.GetTime() + if err != nil { + app.errLogger.Printf("warn: install: failed to fetch UPS time (%s), you should manually verify the time is correct on the UPS", err) + } else if upsT.After(time.Now().Add(1*time.Hour)) || upsT.Before(time.Now().Add(-1*time.Hour)) { + app.errLogger.Printf("warn: install: UPS clock skew detected (this system's time is %s vs. UPS time %s", time.Now().Format(timeLoggingFormat), upsT.Format(timeLoggingFormat)) + } else { + app.stdLogger.Printf("install: UPS clock appears correct (%s)", upsT.Format(timeLoggingFormat)) + } + // install SSL Cert err = client.InstallSSLCert(keyP15, certPem, keyCertP15) if err != nil {