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
18 changes: 16 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ jobs:
steps:

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: ^1.15

- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v5

- name: Check if modules are tidy
run: |
go mod tidy
if [ -n "$(git status --porcelain go.mod go.sum)" ]; then
echo "go.mod or go.sum are not tidy. Please run 'go mod tidy' locally and commit changes."
git diff go.mod go.sum
exit 1
fi

- name: Verify formatting
# 'gofmt -l .' to list files whose formatting differs from gofmt's
# 'test -z' to verify that the output is empty
run: test -z "$(gofmt -l .)"

- name: Build
run: go build -v ./...
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ jobs:
steps:

- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v5

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v8
with:
version: v1.29
version: v2.1.6
23 changes: 11 additions & 12 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ jobs:
- 14000:14000 # ACME API
- 15000:15000 # Management API
steps:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ^1.15
id: go

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Check out code
uses: actions/checkout@v5

- name: Check out code
uses: actions/checkout@v2
- name: Install
run: go install -v ./...

- name: Install
run: go install -v ./...

- name: Run ci.script.txt
run: acmeshell -pebble -autoregister=false -account="" -in test/ci.script.txt
- name: Run ci.script.txt
run: acmeshell -pebble -autoregister=false -account="" -in test/ci.script.txt
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
steps:

- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: ^1.15

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ website/public

# Default -account file for acmeshell
acmeshell.account.json

# Goland/Jetbrains IDE
/.idea
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ An interactive shell designed for [RFC 8555][acme] ACME client/server
developers to use for tests, day to day tasks, and exploring the protocol.
ACMEShell Supports both interactive and non-interactive usage.

> [!IMPORTANT]
> This repository is _very_ lightly maintained. If you're using this code,
> consider offering to help maintain it.

---

[![CI Status](https://github.com/cpu/acmeshell/workflows/Go/badge.svg)](https://github.com/cpu/acmeshell/actions?query=workflow%3AGo)
Expand Down
4 changes: 2 additions & 2 deletions acme/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type Client struct {
net *acmenet.ACMENet
// directory is an in-memory representation of the ACME server's directory
// object.
directory map[string]interface{}
directory map[string]any
// nonce is the value of the last-seen ReplayNonce header from the ACME
// server's HTTP responses. It will be used for the next signing operation.
nonce string
Expand Down Expand Up @@ -296,7 +296,7 @@ func NewClient(config ClientConfig) (*Client, error) {
}

// TODO(@cpu): This is stupid
func (c *Client) Printf(format string, vals ...interface{}) {
func (c *Client) Printf(format string, vals ...any) {
log.Printf(format, vals...)
}

Expand Down
6 changes: 3 additions & 3 deletions acme/client/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
"log"
)

func (c *Client) getDirectory() (map[string]interface{}, error) {
func (c *Client) getDirectory() (map[string]any, error) {
url := c.DirectoryURL.String()

resp, err := c.net.GetURL(url)
if err != nil {
return nil, err
}

var directory map[string]interface{}
var directory map[string]any
err = json.Unmarshal(resp.RespBody, &directory)
if err != nil {
return nil, err
Expand All @@ -26,7 +26,7 @@ func (c *Client) getDirectory() (map[string]interface{}, error) {
// returns it deserialized as a map.
//
// See https://tools.ietf.org/html/rfc8555#section-7.1.1
func (c *Client) Directory() (map[string]interface{}, error) {
func (c *Client) Directory() (map[string]any, error) {
if c.directory == nil {
if err := c.UpdateDirectory(); err != nil {
return nil, err
Expand Down
14 changes: 10 additions & 4 deletions acme/client/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/cpu/acmeshell/acme/keys"

jose "gopkg.in/square/go-jose.v2"
jose "github.com/go-jose/go-jose/v4"
)

// SigningOptions allows specifying signature related options when calling an
Expand Down Expand Up @@ -137,7 +137,7 @@ func signEmbedded(url string, data []byte, opts SigningOptions) (*SignResult, er
signer, err := jose.NewSigner(signingKey, &jose.SignerOptions{
NonceSource: opts.NonceSource,
EmbedJWK: true,
ExtraHeaders: map[jose.HeaderKey]interface{}{
ExtraHeaders: map[jose.HeaderKey]any{
"url": url,
},
})
Expand All @@ -158,7 +158,7 @@ func signKeyID(url string, data []byte, opts SigningOptions) (*SignResult, error

joseOpts := &jose.SignerOptions{
NonceSource: opts.NonceSource,
ExtraHeaders: map[jose.HeaderKey]interface{}{
ExtraHeaders: map[jose.HeaderKey]any{
"url": url,
},
}
Expand All @@ -181,7 +181,7 @@ func sign(signer jose.Signer, url string, data []byte, opts SigningOptions) (*Si

// Reparse the serialized body to get a fully populated JWS object to log
var parsedJWS *jose.JSONWebSignature
parsedJWS, err = jose.ParseSigned(string(serialized))
parsedJWS, err = jose.ParseSigned(string(serialized), goodJWSSignatureAlgorithms)
if err != nil {
return nil, err
}
Expand All @@ -193,3 +193,9 @@ func sign(signer jose.Signer, url string, data []byte, opts SigningOptions) (*Si
SerializedJWS: serialized,
}, nil
}

// Matched to Pebble. Could reasonably be expanded if there was demand.
// https://github.com/letsencrypt/pebble/blob/38069cc4cf29acb188c01f6df9d658d8ace3427a/wfe/jose.go#L18
var goodJWSSignatureAlgorithms = []jose.SignatureAlgorithm{
jose.RS256, jose.ES256, jose.ES384, jose.ES512,
}
4 changes: 2 additions & 2 deletions acme/client/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (c *Client) RefreshNonce() error {
nonceURL, ok := c.GetEndpointURL(acme.NEW_NONCE_ENDPOINT)
if !ok {
return fmt.Errorf(
"Missing %q entry in ACME server directory", acme.NEW_NONCE_ENDPOINT)
"missing %q entry in ACME server directory", acme.NEW_NONCE_ENDPOINT)
}

if c.Output.PrintNonceUpdates {
Expand All @@ -43,7 +43,7 @@ func (c *Client) RefreshNonce() error {
}

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%q returned HTTP status %d, expected %d\n",
return fmt.Errorf("%q returned HTTP status %d, expected %d",
acme.NEW_NONCE_ENDPOINT, resp.StatusCode, http.StatusOK)
}

Expand Down
8 changes: 4 additions & 4 deletions acme/client/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/cpu/acmeshell/acme/resources"
"github.com/cpu/acmeshell/net"

jose "gopkg.in/square/go-jose.v2"
jose "github.com/go-jose/go-jose/v4"
)

// CreateAccount creates the given Account resource with the ACME server.
Expand All @@ -36,7 +36,7 @@ func (c *Client) CreateAccount(acct *resources.Account) error {
}
if acct.ID != "" {
return fmt.Errorf(
"create: account already exists under ID %q\n", acct.ID)
"create: account already exists under ID %q", acct.ID)
}

newAcctReq := struct {
Expand Down Expand Up @@ -67,7 +67,7 @@ func (c *Client) CreateAccount(acct *resources.Account) error {
Signer: acct.Signer,
})
if err != nil {
return fmt.Errorf("create: %s\n", err)
return fmt.Errorf("create: %s", err)
}

log.Printf("Sending %q request (contact: %s) to %q",
Expand Down Expand Up @@ -191,7 +191,7 @@ func (c *Client) CreateOrder(order *resources.Order) error {
// Sign the new order request with the active account
signResult, err := c.Sign(newOrderURL, reqBody, nil)
if err != nil {
return fmt.Errorf("createOrder: %s\n", err)
return fmt.Errorf("createOrder: %s", err)
}

resp, err := c.PostURL(newOrderURL, signResult.SerializedJWS)
Expand Down
2 changes: 1 addition & 1 deletion acme/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"encoding/pem"
"fmt"

jose "gopkg.in/square/go-jose.v2"
jose "github.com/go-jose/go-jose/v4"
)

func sigAlgForKey(signer crypto.Signer) jose.SignatureAlgorithm {
Expand Down
12 changes: 6 additions & 6 deletions acme/resources/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"

"github.com/cpu/acmeshell/acme/keys"
)
Expand Down Expand Up @@ -36,16 +36,16 @@ import (
type Account struct {
// The server assigned Account ID. This is used for the JWS KeyID when
// authenticating ACME requests using the Account's registered keypair.
ID string
ID string `json:"id"`
// If not nil, a slice of one or more email addresses to be used as the ACME
// Account's "mailto://" Contact addresses.
Contact []string
Contact []string `json:"contact"`
// A signer to use to sign protocol messages and to access the ACME account's
// public key
Signer crypto.Signer
// If not nil, a slice of URLs for Order resources the Account created with
// the ACME server.
Orders []string
Orders []string `json:"orders"`
// The JSON path backing the account (if any)
jsonPath string
}
Expand Down Expand Up @@ -124,7 +124,7 @@ func SaveAccount(path string, account *Account) error {
account.jsonPath = path
// write the serialized data to the provided filepath using a mode that only
// allows access to the current user. This file contains a private key!
return ioutil.WriteFile(path, frozenBytes, 0600)
return os.WriteFile(path, frozenBytes, 0600)
}

type rawAccount struct {
Expand Down Expand Up @@ -162,7 +162,7 @@ func (a *Account) save() ([]byte, error) {
// returned.
func RestoreAccount(path string) (*Account, error) {
acct := &Account{}
frozenBytes, err := ioutil.ReadFile(path)
frozenBytes, err := os.ReadFile(path)
if err != nil {
return acct, err
}
Expand Down
16 changes: 8 additions & 8 deletions acme/resources/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ package resources
// the identifier value represented without the "*." prefix.
type Identifier struct {
// The Type of the Identifier value.
Type string
Type string `json:"type"`
// The Identifier value.
Value string
Value string `json:"value"`
}

// The ACME Authorization resource represents an Account's authorization to
Expand All @@ -34,27 +34,27 @@ type Identifier struct {
// https://tools.ietf.org/html/rfc8555#section-7.1.6
type Authorization struct {
// The server-assigned ID (typically a URL) identifying the Authorization.
ID string
ID string `json:"id"`
// The status of this authorization. Possible values are: “pending”, “valid”,
// “invalid”, “deactivated”, “expired”, and “revoked”.
// See:
// https://tools.ietf.org/html/rfc8555#section-7.1.6
Status string
Status string `json:"status"`
// The identifier that the account holding this Authorization is authorized to
// represent
Identifier Identifier
Identifier Identifier `json:"identifier"`
// For pending authorizations, the challenges that the client can fulfill in
// order to prove possession of the identifier. For valid authorizations, the
// challenge that was validated. For invalid authorizations, the challenge
// that was attempted and failed.
Challenges []Challenge
Challenges []Challenge `json:"challenges"`
// A string representing a RFC 3339 date at which time the Authorization is
// considered expired by the server.
Expires string
Expires string `json:"expires"`
// For authorizations created as a result of a newOrder request containing
// a DNS identifier with a value that contained a wildcard prefix this field
// MUST be present, and true
Wildcard bool
Wildcard bool `json:"wildcard"`
}

// String returns the Authorization's server-assigned ID.
Expand Down
10 changes: 5 additions & 5 deletions acme/resources/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ package resources
// https://tools.ietf.org/html/rfc8555#section-7.1.6
type Challenge struct {
// The Type of the challenge (expected values include "http-01", "dns-01", "tls-alpn-01")
Type string
Type string `json:"type"`
// The URL/ID of the challenge (provided by the server in the associated
// Authorization)
//
// TODO(@cpu): This should be renamed to ID for consistency with
// Authorization, Order and Account.
URL string
URL string `json:"url"`
// The Token used for constructing the challenge response for this challenge.
Token string
Token string `json:"token"`
// The Status of the challenge.
Status string
Status string `json:"status"`
// The Error associated with an invalid challenge
Error *Problem `json:",omitempty"`
Error *Problem `json:"error,omitempty"`
}

// String returns the URL of the Challenge.
Expand Down
Loading
Loading