Skip to content

Conversation

@mbecker20
Copy link
Member

@mbecker20 mbecker20 commented Sep 28, 2025

  • 🚨 Both Core and Periphery must be upgraded to v2 for the connection to work.
  • 🚨 Action users: komodo.execute_terminal / komodo.execute_container_exec changes -- see Terminals section below.

Changelog

I'm very excited to bring you Komodo v2. The major improvements are:

  • Enable "reversing" the Periphery connection.
  • Easy upgrade to public key authentication.
  • Implement Server onboarding flow.
  • Improve the Terminal feature set.

Periphery → Core Connection 🦎

One of the major limitations in Komodo v1 was that Core always had to establish the connection to Periphery. In many setups, this was not the optimal connection strategy, and would often require the usage of a VPN to establish the connection securely.

Now with v2, Periphery can be configured to establish an outbound connection to Core, and communication with Core will be multiplexed over this connection. #281

Existing Core → Periphery connections also continue working, and no configuration changes are required to update from Komodo v1.

Ideally Komodo Core should be served over HTTPS with certificates recognized by Periphery, however it also works for Periphery to connect to Core over ws://..., as well as insecure TLS (untrusted certificates) if you set PERIPHERY_CORE_TLS_INSECURE_SKIP_VERIFY=true.

It is usually both easier and more secure to use this Periphery → Core connection method, and there isn't much other difference or downside. The default compose configuration has changed to this method, and I recommend most users migrate their setups when convenient.

periphery.config.toml

## The address of Komodo Core. Must be reachable from this host.
## If provided, Periphery will operate in outbound mode.
## Use `ws://12.34.56.78:9120` if you don't have TLS enabled Core.
## Env: PERIPHERY_CORE_ADDRESS
## Default: None
core_address = "example.komodo.com"

## The Server this Periphery agent should connect as.
## If matching Server doesn't exist yet, see "Server Onboarding".
## Using `$(hostname)` will use the output of shell `hostname` command.
## Env: PERIPHERY_CONNECT_AS
## Default: None
connect_as = "$(hostname)"

If using a reverse proxy with Komodo Core, you can limit the IPs which can connect to the Periphery endpoint. For example with Caddy:

(reject-ips) {
	@externalIp not remote_ip 192.168.0.0/16 12.34.56.78/32
	respond @externalIp 403
}

komodo.example.com {
	handle /ws/periphery {
		import reject-ips
		reverse_proxy komodo-core:9120
	}
	handle {
		reverse_proxy komodo-core:9120
	}
}

Private / Public Key Authentication 🦎

The other limitation was the usage of a passkey for Core to authenticate to Periphery. This was not a secure usage pattern for 2 reasons:

  1. The passkey had to be sent over the network rather than using cryptographic methods.
  2. A single compromised Server would expose the passkey, which could be used to compromise other Servers.

While this passkey pattern remains supported for backward compatibility (only Core -> Periphery direction), users looking to move to Periphery -> Core (or increase Core -> Periphery security) can now replace their passkeys with paired private / public keys.

If you point to key files that don't exist yet, they will be generated. While the keys are openssl compatible, there is no need to manually generate keys.

core.config.toml

## Private key to use with Noise handshake to authenticate with Periphery agents.
## To load from file, use `file:/config/keys/core.key`, and mount `- keys:/config/keys`.
## If it doesn't exist yet, Core will generate a new key pair and store it for persistence.
## Env: KOMODO_PRIVATE_KEY or KOMODO_PRIVATE_KEY_FILE
private_key = "file:/config/keys/core.key"

periphery.config.toml

## Private key to use with Noise handshake to authenticate with Periphery agents.
## To load from file, use `file:/path/to/periphery.key`.
## If it doesn't exist yet, Periphery will generate a new key pair and store it for persistence.
private_key = "file:/config/keys/periphery.key"

## Optional. Accepted Core public key to allow connection.
## To load from file, use `file:/path/to/core.pub`
## Env: PERIPHERY_CORE_PUBLIC_KEY
core_public_key = "file:/config/keys/core.pub"

The authentication is accomplished using a Noise XX handshake via the snow library.

Automatic Key Rotation 🦎

Storing the private keys in dedicated files allows for easy automatic rotation of key pairs. To rotate a Server's key, Core sends the RotatePrivateKey request to Periphery, which then generates a new key pair, and returns the public key back to Core to update the allowed public key in the Database. The new private key stays local to each Periphery agent and is never exposed to the network. Note that it only works for Servers that are currently connected, if a Server cannot be reached, the rotation will be skipped for that Server.

Similar to the "Backup Core Database" and "Global Auto Update" default system Procedures, one for key rotation will now be generated during first database initialization. Here is the TOML:

[[procedure]]
name = "Rotate Server Keys"
description = "Rotates all currently connected Server keys."
tags = ["system"]
config.schedule = "Every day at 06:00"

[[procedure.config.stage]]
name = "Stage 1"
enabled = true
executions = [
  { execution.type = "RotateAllServerKeys", execution.params = {}, enabled = true }
]

Server Onboarding 🦎

You can now make Onboarding Keys in Settings / Onboarding page. Giving one of these to Periphery allows itself to "onboard" as a new Server in Komodo. These keys are only used for this purpose, and cannot be used as the Periphery private key itself. They aren't needed if the Server already exists.

If you would like, you can make multiple onboarding keys, and depending on the key used to onboard, the created Server will inherit the configured Tags, as well as the configuration from the optional template Server.

periphery.config.toml

## Make Onboarding Keys in Server settings.
## Not needed if Server already exists.
## Env: PERIPHERY_ONBOARDING_KEY
onboarding_key = "MC4CAQAwBQYDK2VuBCIEIH1+pLGMYkT36gNj5fRWrgRenBjaTfuUVoScW1a9XG8y"
Screenshot 2025-10-05 at 3 01 34 PM

Terminals

In v1, the Terminal feature set grew slowly over the releases, and the implementations needed refinement. For example, Servers could create multiple Terminal tabs, but the Container was limited to a single Terminal. Additionally, access to Terminals was hidden behind multiple clicks -- first navigate to Server / Container, then Terminal tab, etc.

The issue stemmed from the implementation placing "Container" type Terminals as a subset of "Server" type Terminals. This is now refactored to "flatten" the implementation -- There is now a single "Terminal" type, with possible sub-types being "Server" or "Container". In other words, the different Terminal types move from a "Parent - Child" relationship to a "Sibling" relationship, making the implementations between Server and Container sub-types more straightforward and consistent.

The first thing to notice is the new dedicated "Terminals" page. This provides a birds-eye view of active Terminal sessions for easier management. This page includes all the expected "Create", "Connect", "Delete" functionality, enabling usage of this feature entirely from this page.

Screenshot 2025-10-28 at 12 50 25 PM
  • Container type terminals (so also Stack service / Deployment) now support multiple active Terminals. The UI is now identical to the Server terminals for maximum flexibility.
  • docker attach ... is now supported.
  • The Komodo CLI now supports direct ssh analog -- km ssh <SERVER>.
Screenshot 2025-10-28 at 12 53 56 PM

🚨Breaking Action change

Instead of komodo.execute_terminal and komodo.execute_container_exec, use the updated methods

await komodo.execute_server_terminal(
  {
    server: "my-server",
    terminal: "custom-name",
    command: "ls -l",
    // New. Provide session init args.
    init: {
       command: "bash",
       recreate: Types.TerminalRecreateMode.Always
    }
  },
  // Callbacks unchanged
  {
    onLine: console.log
  }
);
await komodo.execute_container_terminal(
  {
    server: "my-server",
    container: "my-container",
    terminal: "custom-name",
    command: "ls -l",
    init: {
       command: "sh",
       recreate: Types.TerminalRecreateMode.Always
    }
  },
  // Callbacks unchanged
  {
    onLine: console.log
  }
);
// Also can use komodo.execute_stack_service_terminal and komodo.execute_deployment_terminal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants