Skip to content

Commit

Permalink
Signer authorized keys (#610)
Browse files Browse the repository at this point in the history
* Support for Signer Authorized Keys

Authorized key is the tezos native method to authenticate signing
requests, one that we use in the new tezos-kms-signer-lambda.

This adds the required support on tezos-k8s to sign with such a signer.

The way it works in octez is:

* when the baker/client connects to the signer for the first time,
  signer answers with a list of "authorized_keys" that the signature
  request must be signed with. These authorized keys are just tezos
  accounts
* if the baker/client has the secret key for one of these authorized
  keys, they will just sign every request with it. otherwise, there will
  be an error
* this can't be nested. the authorized_key can't be remote

We add support in tezos-k8s by assuming the authorized_keys are just
standard "accounts". Then, you may configure a baker as follows:

```
nodes:
  mybaker:
    bake_using_accounts:
    - mybakeraddy
    authorized_keys:
    - my_authorized_key
```

config-generator then ensures that the private authorized key is
accessible to the baker.

We also add support on octez-signer end:

```
octezSigners:
  mysigner:
    sign_for_accounts:
    - mybakeraddy
    authorized_keys:
    - my_authorized_key
```

When set, the signer mandates requests to be authenticated. Otherwise,
it signs anything.

This way, you can test end-to-end in a private chain.

We modify mkchain to do this by default: mkchain now generates an
authorized key and uses it to sign by default.

Also, mkchain was previously defaulting to using one remote signer, but
this broke when adding support for tacoInfra signer. I fixed it.

I have tested it with 3 bakers and 2 signers, one authorized and one
not. It's all working. I haven't tried zerotier and public chains.

Other changes:

* switch default version to 17.3
* no magic byte restriction from signer - prevents activation

* Update mkchain/tqchain/mkchain.py

Co-authored-by: Aryeh Harris <harryttd@users.noreply.github.com>

* Update charts/tezos/values.yaml

Co-authored-by: Aryeh Harris <harryttd@users.noreply.github.com>

* comment phrasing, per review

* fix comments per review

* validate in helm that authroized keys exist

* Update charts/tezos/templates/_helpers.tpl

Co-authored-by: Aryeh Harris <harryttd@users.noreply.github.com>

---------

Co-authored-by: Aryeh Harris <harryttd@users.noreply.github.com>
  • Loading branch information
nicolasochem and harryttd authored Nov 6, 2023
1 parent cc77815 commit 9d1750c
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 22 deletions.
6 changes: 5 additions & 1 deletion charts/tezos/scripts/remote-signer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ CLIENT_DIR="$TEZ_VAR/client"
NODE_DIR="$TEZ_VAR/node"
NODE_DATA_DIR="$TEZ_VAR/node/data"

CMD="$TEZ_BIN/octez-signer -d $CLIENT_DIR launch http signer --magic-bytes 0x11,0x12,0x13 --check-high-watermark -a 0.0.0.0 -p 6732"
extra_args=""
if [ -f ${CLIENT_DIR}/authorized_keys ]; then
extra_args="${extra_args} --require-authentication"
fi
CMD="$TEZ_BIN/octez-signer -d $CLIENT_DIR ${extra_args} launch http signer -a 0.0.0.0 -p 6732"

# ensure we can run tezos-signer commands without specifying client dir
ln -s /var/tezos/client /home/tezos/.tezos-signer
Expand Down
28 changes: 28 additions & 0 deletions charts/tezos/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,31 @@ metadata:
{{- end }}
{{- "true" }}
{{- end }}

{{/*
Get list of authorized keys. Fails if any of the keys is not defined in the accounts.
*/}}
{{- define "tezos.getAuthorizedKeys" }}
{{- $allAuthorizedKeys := list }}
{{- /* Gather keys from nodes */}}
{{- range $node := .Values.nodes }}
{{- range $instance := $node.instances }}
{{- if .authorized_keys }}
{{- $allAuthorizedKeys = concat $allAuthorizedKeys .authorized_keys }}
{{- end }}
{{- end }}
{{- end }}
{{- /* Gather keys from octezSigners */}}
{{- range $signer := .Values.octezSigners }}
{{- if $signer.authorized_keys }}
{{- $allAuthorizedKeys = concat $allAuthorizedKeys $signer.authorized_keys }}
{{- end }}
{{- end }}
{{- /* Ensure all keys are defined in accounts and fail otherwise */}}
{{- $allAuthorizedKeys = uniq $allAuthorizedKeys }}
{{- range $key := $allAuthorizedKeys }}
{{- if not (index $.Values.accounts $key "key") }}
{{- fail (printf "Authorized key '%s' is not defined in accounts." $key) }}
{{- end }}
{{- end }}
{{- end }}
1 change: 1 addition & 0 deletions charts/tezos/templates/configs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,4 @@ metadata:
namespace: {{ .Release.Namespace }}
---
{{- end }}
{{- include "tezos.getAuthorizedKeys" . }}
13 changes: 12 additions & 1 deletion charts/tezos/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ is_invitation: false

# Images not part of the tezos-k8s repo go here
images:
octez: tezos/tezos:v17.1
octez: tezos/tezos:v17.3
tacoinfraRemoteSigner: ghcr.io/oxheadalpha/tacoinfra-remote-signer:0.1.0
# Images that are part of the tezos-k8s repo go here with 'dev' tag
tezos_k8s_images:
Expand Down Expand Up @@ -174,6 +174,11 @@ should_generate_unsafe_deterministic_data: false
# Don't also set `bake_using_accounts`.
# - `bake_using_accounts`: List of account names that should be used for baking.
# Don't also set `bake_using_account`.
# - `authorized_keys`: List of account names that should be used as keys to
# authenticate a baker to a signer.
# When a baker uses a remote signer that requires
# authentication, the relevant key from this list
# will be used to sign every signature request.
# - `config`: Same as the outer statefulset level `config`. It overrides the
# statefulset level.
# - `is_bootstrap_node`: Boolean for is this node a bootstrap peer.
Expand Down Expand Up @@ -314,6 +319,12 @@ octezSigners: {}
# tezos-signer-0:
# accounts:
# - baker0
# authorized_keys:
# # Names of accounts used to authenticate the baker to the signer.
# # The baker must have the private key for one of the listed
# # accounts. The signer will only sign a request from a baker
# # authenticated by an allowed key.
# - authorized-key-0
# ```
#
# Deploys a signer using AWS KMS to sign operations.
Expand Down
2 changes: 1 addition & 1 deletion mkchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ You can explicitly specify some values by:
| | --number-of-nodes | Number of non-baking nodes in the cluster | 0 |
| bootstrap_peers | --bootstrap-peers | Peer ips to connect to | [] |
| expected_proof_of_work | --expected-proof-of-work | Node identity generation difficulty | 0 |
| images.octez | --octez-docker-image | Version of the Octez docker image to run | tezos/tezos:v17.1 |
| images.octez | --octez-docker-image | Version of the Octez docker image to run | tezos/tezos:v17.3 |
| | --use-docker (--no...) | Use (or don't use) docker to generate keys rather than pytezos | autodetect |
| zerotier_config.zerotier_network | --zerotier-network | Zerotier network id for external chain access | |
| zerotier_config.zerotier_token | --zerotier-token | Zerotier token for external chain access | |
Expand Down
14 changes: 8 additions & 6 deletions mkchain/tqchain/mkchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def quoted_scalar(dumper, data): # a representer to force quotations on scalars
},
"octez_docker_image": {
"help": "Version of the Octez docker image",
"default": "tezos/tezos:v17.1",
"default": "tezos/tezos:v17.3",
},
"use_docker": {
"action": "store_true",
Expand Down Expand Up @@ -154,6 +154,7 @@ def node_config(name, n, is_baker):
"shell": {"history_mode": "rolling"},
"metrics_addr": [":9932"],
},
"authorized_keys": ["authorized-key-0"],
}
if is_baker:
ret["bake_using_accounts"] = [f"{name}-{n}"]
Expand Down Expand Up @@ -243,7 +244,7 @@ def main():
baking_accounts = {
f"{ARCHIVE_BAKER_NODE_NAME}-{n}": {} for n in range(args.number_of_bakers)
}
for account in baking_accounts:
for account in [*baking_accounts, "authorized-key-0"]:
print(f"Generating keys for account {account}")
keys = gen_key(args.octez_docker_image)
for key_type in keys:
Expand Down Expand Up @@ -275,11 +276,12 @@ def main():
],
}

signers = {
octezSigners = {
"tezos-signer-0": {
"sign_for_accounts": [
"accounts": [
f"{ARCHIVE_BAKER_NODE_NAME}-{n}" for n in range(args.number_of_bakers)
]
],
"authorized_keys": ["authorized-key-0"],
}
}

Expand Down Expand Up @@ -308,7 +310,7 @@ def main():
**base_constants,
"bootstrap_peers": bootstrap_peers,
"accounts": accounts["secret"],
"signers": signers,
"octezSigners": octezSigners,
"nodes": creation_nodes,
**activation,
}
Expand Down
10 changes: 5 additions & 5 deletions test/charts/mainnet.expect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ data:
ARCHIVE_TARBALL_URL: ""
PREFER_TARBALLS: "false"
SNAPSHOT_SOURCE: "https://xtz-shots.io/tezos-snapshots.json"
OCTEZ_VERSION: "tezos/tezos:v17.1"
OCTEZ_VERSION: "tezos/tezos:v17.3"
NODE_GLOBALS: |
{
"config": {},
Expand Down Expand Up @@ -128,7 +128,7 @@ spec:
spec:
containers:
- name: octez-node
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -213,7 +213,7 @@ spec:
memory: 80Mi
initContainers:
- name: config-init
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -324,7 +324,7 @@ spec:
- mountPath: /var/tezos
name: var-volume
- name: snapshot-importer
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -387,7 +387,7 @@ spec:
- mountPath: /var/tezos
name: var-volume
- name: upgrade-storage
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down
10 changes: 5 additions & 5 deletions test/charts/mainnet2.expect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ data:
ARCHIVE_TARBALL_URL: ""
PREFER_TARBALLS: "false"
SNAPSHOT_SOURCE: "https://xtz-shots.io/tezos-snapshots.json"
OCTEZ_VERSION: "tezos/tezos:v17.1"
OCTEZ_VERSION: "tezos/tezos:v17.3"
NODE_GLOBALS: |
{
"config": {},
Expand Down Expand Up @@ -195,7 +195,7 @@ spec:
spec:
containers:
- name: octez-node
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -316,7 +316,7 @@ spec:
memory: 80Mi
initContainers:
- name: config-init
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -433,7 +433,7 @@ spec:
- mountPath: /var/tezos
name: var-volume
- name: snapshot-importer
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down Expand Up @@ -498,7 +498,7 @@ spec:
- mountPath: /var/tezos
name: var-volume
- name: upgrade-storage
image: "tezos/tezos:v17.1"
image: "tezos/tezos:v17.3"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
Expand Down
6 changes: 5 additions & 1 deletion test/charts/private-chain.expect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,11 @@ spec:
NODE_DIR="$TEZ_VAR/node"
NODE_DATA_DIR="$TEZ_VAR/node/data"
CMD="$TEZ_BIN/octez-signer -d $CLIENT_DIR launch http signer --magic-bytes 0x11,0x12,0x13 --check-high-watermark -a 0.0.0.0 -p 6732"
extra_args=""
if [ -f ${CLIENT_DIR}/authorized_keys ]; then
extra_args="${extra_args} --require-authentication"
fi
CMD="$TEZ_BIN/octez-signer -d $CLIENT_DIR ${extra_args} launch http signer -a 0.0.0.0 -p 6732"
# ensure we can run tezos-signer commands without specifying client dir
ln -s /var/tezos/client /home/tezos/.tezos-signer
Expand Down
30 changes: 28 additions & 2 deletions utils/config-generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,17 @@ def expose_secret_key(account_name):
pod. It returns the obvious Boolean.
"""
if MY_POD_TYPE == "activating":
all_authorized_keys = [
key
for node in NODES.values()
for instance in node["instances"]
for key in instance.get("authorized_keys", [])
]
if account_name in all_authorized_keys:
# Populate authorized keys known by all bakers in the activation account.
# This ensures that activation will succeed with a remote signer that requires auth,
# regardless of which baker does it.
return True
return NETWORK_CONFIG["activation_account_name"] == account_name

if MY_POD_TYPE == "signing":
Expand All @@ -340,6 +351,8 @@ def expose_secret_key(account_name):
if MY_POD_TYPE == "node":
if MY_POD_CONFIG.get("bake_using_account", "") == account_name:
return True
if account_name in MY_POD_CONFIG.get("authorized_keys", {}):
return True
return account_name in MY_POD_CONFIG.get("bake_using_accounts", {})

return False
Expand Down Expand Up @@ -419,6 +432,7 @@ def import_keys(all_accounts):
secret_keys = []
public_keys = []
public_key_hashs = []
authorized_keys = []

for account_name, account_values in all_accounts.items():
print("\n Importing keys for account: " + account_name)
Expand Down Expand Up @@ -453,6 +467,12 @@ def import_keys(all_accounts):
public_key_hashs.append({"name": account_name, "value": pkh_b58})
account_values["pkh"] = pkh_b58

if MY_POD_TYPE == "signing" and account_name in MY_POD_CONFIG.get(
"authorized_keys", {}
):
print(f" Appending authorized key: {pk_b58}")
authorized_keys.append({"name": account_name, "value": pk_b58})

print(f" Account key type: {account_values.get('type')}")
print(
f" Account bootstrap balance: "
Expand All @@ -463,17 +483,21 @@ def import_keys(all_accounts):
+ f"{account_values.get('is_bootstrap_baker_account', False)}"
)

sk_path, pk_path, pkh_path = (
sk_path, pk_path, pkh_path, ak_path = (
f"{tezdir}/secret_keys",
f"{tezdir}/public_keys",
f"{tezdir}/public_key_hashs",
f"{tezdir}/authorized_keys",
)
print(f"\n Writing {sk_path}")
json.dump(secret_keys, open(sk_path, "w"), indent=4)
print(f" Writing {pk_path}")
json.dump(public_keys, open(pk_path, "w"), indent=4)
print(f" Writing {pkh_path}")
json.dump(public_key_hashs, open(pkh_path, "w"), indent=4)
if MY_POD_TYPE == "signing" and len(authorized_keys) > 0:
print(f" Writing {ak_path}")
json.dump(authorized_keys, open(ak_path, "w"), indent=4)


def create_node_identity_json():
Expand Down Expand Up @@ -739,7 +763,9 @@ def create_node_snapshot_config_json(history_mode):
]
if octez_version:
matching_snapshots = [
s for s in matching_snapshots if int(octez_version) == s.get("tezos_version").get("version").get("major")
s
for s in matching_snapshots
if int(octez_version) == s.get("tezos_version").get("version").get("major")
]
matching_snapshots = sorted(matching_snapshots, key=lambda s: s.get("block_height"))

Expand Down

0 comments on commit 9d1750c

Please sign in to comment.