Setting the Service mode to SSH enables it to operate in the application-layer aware SSH mode. This mode enables you to provide secretless access for Users to the protected upstream SSH server without having to share the upstream's password or private key. This mode also provides you with clear application-layer aware visibility where your SSH sessions are logged and audited in real-time.
You can also check out the embedded SSH mode (read more here) where you can seamlessly provide secretless SSH access via SSH servers that are embedded from within connected octelium clients to serve directly SSH to Users without the need of SSH servers running on the client's host.
Secretless Access
Secretless access enables you to provide secretless access for authorized Users to SSH-based Service by automatically injecting passwords and private keys to authenticate to the upstream SSH server, and force the User to connect to the upstream as a specific SSH user.
Password
Here is an example where the upstream SSH server is accessed through a password. First, you need to create a Secret to store the password of your upstream SSH server (read more here) as follows:
octeliumctl create secret ssh1-password
Now, you define your Service as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13password:14fromSecret: ssh1-password15# This is the upstream SSH server public key16upstreamHostKey:17key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+G/MMcPZTKlWX...
For internal/private upstream SSH servers behind NAT, you need to remotely serve them via a connected octelium client or container as discussed here.
Private Key
Here is an example where the upstream SSH server is accessed through a private key. First, you need to create a Secret to store the private key of the upstream SSH server (read more here) as follows:
octeliumctl create secret ssh1-private-key --file ~/.ssh/id_ed25519# ORocteliumctl create secret ssh1-private-key -f ~/.ssh/id_ed25519
Now, you define your Service as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13privateKey:14fromSecret: ssh1-private-key15# This is the upstream SSH server public key16upstreamHostKey:17key: ssh-rsa AAA...
If you do not explicitly set a value for the user field, then the value provided by the downstream client will be used.
Upstream Host Key
As shown above, the upstream SSH server public key needs to be provided in the OpenSSH public key format <key-type> <base64-encoded-key-data> [comment] to be verified by the Service SSH client upon connecting to upstream. Here is an example:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13password:14fromSecret: ssh1-password15upstreamHostKey:16key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+G/MMcPZTKlWX...
You can, however, disable checking the upstream's host key via insecureIgnoreHostKey field as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13password:14fromSecret: <SECRET_NAME>15upstreamHostKey:16insecureIgnoreHostKey: true
Access Control
You can control access based on the SSH request information. Such information are stored in ctx.request.ssh where it contains the requested SSH user. Here is an example:
1kind: Service2metadata:3name: svc14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10# rest of config...11authorization:12inlinePolicies:13- spec:14rules:15- effect: ALLOW16condition:17match: ctx.request.ssh.connect.user != "root"
You do not actually need to control access by checking against the SSH user (e.g. root) since you already can override the SSH user in the Service configuration as illustrated above regardless of the SSH user value provided by the User. You can also use dynamic configuration in order to map different Users to different credentials and/or different SSH users. You can read more about dynamic configuration here
Dynamic Configuration
You can use dynamic configuration in order to, for example, route to different upstreams and/or set different users/passwords depending on the request's context (read more about dynamic configuration here). Here is a simple example where Users belonging to the ops Group are logged in as root SSH users, while the rest are logged in as some unprivileged usr SSH user.
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6dynamicConfig:7configs:8- name: root9upstream:10url: ssh://address-to-host11ssh:12user: root13auth:14password:15fromSecret: root-password16upstreamHostKey:17key: ssh-rsa AAA....18- name: usr19upstream:20url: ssh://address-to-host21ssh:22user: usr23auth:24password:25fromSecret: usr-password26upstreamHostKey:27key: ssh-rsa AAA....28rules:29- condition:30match: '"ops" in ctx.user.spec.groups'31configName: root32- condition:33matchAny: true34configName: usr
Subsystems
By default, SSH subsystem requests (e.g. SFTP) are disabled. You can, however, enable support for subsystems via the enableSubsystem field as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13password:14fromSecret: <SECRET_NAME>15enableSubsystem: true
Local Port Forwarding
By default, SSH local port forwarding (read more here) is disabled. You can, however, enable support for local port forwarding via the enableLocalPortForwarding field as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6port: 20227config:8upstream:9url: ssh://address-to-host10ssh:11user: root12auth:13password:14fromSecret: <SECRET_NAME>15enableLocalPortForwarding: true
Visibility
The Service emits access logs in real time to the audit collector. Each log provides SSH application-layer aware information which includes full session recording as well as extracting exec requests and port forwarding session events. Here is an example:
1{2"apiVersion": "core/v1",3"kind": "AccessLog",4"metadata": {5"id": "f81f-uvw5-ltpjsqrmamkfwkesx8yf1qds-yqxy-ykr2",6"createdAt": "2025-09-10T22:20:12.539582802Z",7"actorRef": {8"apiVersion": "core/v1",9"kind": "Session",10"uid": "b1bc6aaa-df51-456d-aa37-b77377ea26f0",11"name": "root-1x09ce",12"resourceVersion": "019935b6-5aaa-791f-b0bb-34668298c1fa"13},14"targetRef": {15"apiVersion": "core/v1",16"kind": "Service",17"uid": "2898082e-7eab-4250-ad1e-694d8f995e74",18"name": "ssh-h01.default",19"resourceVersion": "019935b6-5acf-7a00-97d2-69ca402d873f"20}21},22"entry": {23"common": {24"startedAt": "2025-09-10T22:19:56.701591026Z",25"endedAt": "2025-09-10T22:20:12.539584852Z",26"status": "ALLOWED",27"mode": "SSH",28"sessionRef": {29"apiVersion": "core/v1",30"kind": "Session",31"uid": "b1bc6aaa-df51-456d-aa37-b77377ea26f0",32"name": "root-1x09ce",33"resourceVersion": "019935b6-5aaa-791f-b0bb-34668298c1fa"34},35"userRef": {36"apiVersion": "core/v1",37"kind": "User",38"uid": "d72a39da-6f1c-43f7-ad75-dbaf76111b10",39"name": "root",40"resourceVersion": "019934c7-3d99-7864-8ccf-abe9eadfe023"41},42"serviceRef": {43"apiVersion": "core/v1",44"kind": "Service",45"uid": "2898082e-7eab-4250-ad1e-694d8f995e74",46"name": "ssh-h01.default",47"resourceVersion": "019935b6-5acf-7a00-97d2-69ca402d873f"48},49"namespaceRef": {50"apiVersion": "core/v1",51"kind": "Namespace",52"uid": "2073e6f7-24c2-49d2-b0df-e5cd3636d82c",53"name": "default",54"resourceVersion": "019934c7-6d7a-73cd-a510-dfadfdfa6682"55},56"regionRef": {57"apiVersion": "core/v1",58"kind": "Region",59"uid": "85477de2-67d3-48ed-bda7-6c914489badf",60"name": "default"61},62"connectionID": "og0t-9vd4-m6ancokp4o13icnn0qlf05kv-0ph9-mkxf",63"sessionID": "og0t-9vd4-m6ancokp4o13icnn0qlf05kv-0ph9-mkxf-rnfoex",64"sequence": 3965},66"info": {67"ssh": {68"type": "SESSION_RECORDING",69"sessionRecording": {70"type": "STDOUT",71"data": "ABCDEF...."72}73}74}75}76}
As you can see in the above example, the type of this SSH access Log is a SESSION_RECORDING. The SSH mode has currently 10 Log types: START, END, DIRECT_TCPIP_START, DIRECT_TCPIP_END, SESSION_START, SESSION_END, SESSION_RECORDING, SESSION_REQUEST_SHELL, SESSION_REQUEST_EXEC, SESSION_REQUEST_SUBSYSTEM and some of these types include different detailed information according to their type. You can read more in the API reference.
Visibility Options
You can disable the SSH session recording altogether via the disableSessionRecording field as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6config:7upstream:8url: ssh://address-to-host9ssh:10# The rest of your configs11visibility:12disableSessionRecording: true
By default, Vigil does not log stdin as it is usually echoed by stdout, unless for the case of non-interactive sessions. You can, however, explicitly enable STDIN recording as follows:
1kind: Service2metadata:3name: ssh14spec:5mode: SSH6config:7upstream:8url: ssh://address-to-host9ssh:10# The rest of your configs11visibility:12enableSessionStdinRecording: true