Skip to content

Commit

Permalink
SSL and Authorization support (server, client and proxy) (#9)
Browse files Browse the repository at this point in the history
* ssl  support
  * mkitd server - now defaults to https
  * haproxy template - see README for details on configuring port publishing
* add authorization support
  * mkitd clients must set X-API-KEY header with key provisioned on mkitd server configuration
  * MKIt client updated to support server authorization

---------

Co-authored-by: Vasco Santos <7835679+valexsantos@users.noreply.github.com>
  • Loading branch information
valexsantos and Vasco Santos committed Feb 24, 2024
1 parent 1bae893 commit dc01d25
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 157 deletions.
189 changes: 110 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This is micro kubernetes(tm) on Ruby(tm), a simple tool to deploy containers to mimic a (very) minimalistic k8 cluster with a nice REST API.

It's also a frontend for `docker`, providing an easier way for your services to be locally available, without the need to care about local `ports` availability.

It contains an internal DNS and uses HAProxy for routing/balancing/fail-over for Pods access.
The database is a simple sqlite3 db and the server is a Sinatra based application.

Expand All @@ -16,91 +18,24 @@ The daemon is responsible for HAProxy pods routing configuration. It also provid
* Docker
* Linux (iproute2 package)

**Note:** in order to have **ssl support**, you must install `openssl-dev` package (e.g. `libssl-dev` on Ubuntu) prior to install MKIt gem.

## Install

This is a simple ruby gem, so to install run
This is a simple ruby gem, so to install execute:
```
# gem install mkit
```

## Running

The `daemon` requires `root` user (due to `ip` and `haproxy`), you can run it directly on the repository root...

```
# ./mkitd --help
Usage: mkitd [options]
-c config-dir set the config dir (default is /etc/mkit)
-p port set the port (default is 4567)
-b bind specify bind address (e.g. /tmp/app.sock)
-s server specify rack server/handler
-q turn on quiet mode (default is off)
-x turn on the mutex lock (default is off)
-e env set the environment (default is development)
-o addr set the host (default is (env == 'development' ? 'localhost' : '0.0.0.0'))
```

or after the `gem install mkit-<version>.gem`. The server and client will be installed on host.

```
# mkitd
...
0.65s info: MKIt is up and running! [ec=0xbe0] [pid=45804] [2023-12-29 15:46:04 +0000]
```

There's also samples on the samples dir, for `daemontools` and `systemd`.

### Accessing the API

A client is provided to interact with `mkit server`.

Run `mkitc help` for a list of current supported commands.

```
Usage: mkitc <command> [options]
Micro k8s on Ruby - a simple tool to mimic a (very) minimalistic k8 cluster
Commands:
ps show services status (alias for status)
status show services status
logs prints service logs
start start service
stop stop service
restart restart service
create create new service
update update service
rm remove service
version prints mkit server version
proxy haproxy status and control
profile mkit client configuration profile
Run 'mkitc help <command>' for specific command information.
```

Example:

```
$ mkitc ps postgres
+----+----------+---------------+----------+--------------+---------+
| id | name | addr | ports | pods | status |
+----+----------+---------------+----------+--------------+---------+
| 2 | postgres | 10.210.198.10 | tcp/4532 | 49b5e4c8f247 | RUNNING |
+----+----------+---------------+----------+--------------+---------+
```
The service `postgres` is available on IP `10.210.198.10:5432`

## Configuration

### Server configuration

On startup, configuration files on `config` directory will be copied to `/etc/mkit`.
On startup, [the server configuration](config) will be created on `/etc/mkit`.

The server is available by default on `http://localhost:4567` but you can configure server startup parameters on `/etc/mkit/mkitd_config.sh`
The server will available by default on `https://localhost:4567` but you can configure server startup parameters on `/etc/mkit/mkitd_config.sh`

Please check `samples/systemd` or `samples/daemontools` directories for more details.
Please check [systemd](samples/systemd) or [daemontools](samples/daemontools) directories for more details.

```
# /etc/mkit/mkitd_config.sh
Expand All @@ -110,7 +45,7 @@ Please check `samples/systemd` or `samples/daemontools` directories for more det
OPTIONS=""
# e.g. OPTIONS="-b 0.0.0.0"
```
HAProxy config directory and control commands are defined on `mkit_config.yml`
HAProxy config directory and control commands are defined on [mkit_config.yml](config/mkit_config.yml)

```
# /etc/mkit/mkit_config.yml - mkit server configuration file.
Expand All @@ -127,9 +62,13 @@ mkit:
status: systemctl status haproxy
database:
env: development
clients:
- id: client_1_id
- id: client_2_id
- ...
```

You must configure `haproxy` to use config directory. e.g. on Ubuntu
You must configure `haproxy` to use config directory. for example on Ubuntu:

```
# /etc/default/haproxy
Expand All @@ -145,19 +84,40 @@ CONFIG="/etc/haproxy/haproxy.d"
# Add extra flags here, see haproxy(1) for a few options
#EXTRAOPTS="-de -m 16"
```

#### Authorization

To access MKIt server API, you must add each client `id` to server configuration:

```
# /etc/mkit/mkit_config.yml - mkit server configuration file.
mkit:
my_network:
...
clients:
- id: client_1_id
- id: client_2_id
- ...
```

### Client configuration

On `mkitc` first call, default configuration will be copied to `$HOME/.mkit` with `local`default profile set.

You can add more servers and change active profile with `$mkitc profile set <profile_name>`, e.g. `$mkitc profile set server_2`
You must call `mkitc init` to initialize client configuration.

Client identification key (`my_id`) will be generated, printed out to console and saved to the client's configuration file.

You may edit the local configuration file to add more servers and change active profile with `$mkitc profile set <profile_name>`, e.g. `$mkitc profile set server_2`

```
# ~/.mkit/mkitc_config.yml
mkit:
local:
server.uri: http://localhost:4567
server.uri: https://localhost:4567
server_2: # you can add more servers. change the client active profile with mkitc profile command
server.uri: http://192.168.29.232:4567
server.uri: https://192.168.29.232:4567
my_id: unique_id # this id is generated running mkitc init
```

### Service
Expand All @@ -171,7 +131,12 @@ service:
# <external_port>:[internal_port]:<tcp|http>:[round_robin (default)|leastconn]
# to define a range on `external_port`, leave `internal_port` blank
# - 5000-5100::tcp:round_robin
# range on `internal_port` is not supported
# range on `internal_port` is not supported
# ssl suport
# <external_port>:[internal_port]:<tcp|http>:round_robin|leastconn[:ssl[,<cert.pem>(mkit.pem default)>]]
# e.g.
# - 443:80:http:round_robin:ssl # uses mkitd default crt file (mkit.pem)
# - 443:80:http:round_robin:ssl,/etc/pki/foo.pem # custom crt file full path
- 5672:5672:tcp:round_robin
- 80:15672:http:round_robin
resources:
Expand All @@ -186,6 +151,72 @@ service:
RABBITMQ_DEFAULT_VHOST: mkit
```

## Running

The `mkitd server daemon` requires `root` user (due to `ip` and `haproxy`).
After installing the gem, server and client will be available on host.
```
# mkitd --help
Usage: mkitd [options]
-c config-dir set the config dir (default is /etc/mkit)
-p port set the port (default is 4567)
-b bind specify bind address (e.g. 0.0.0.0)
-e env set the environment (default is development)
-o addr alias for '-b' option
--no-ssl disable ssl - use http for local server. (default is https)
--ssl-key-file PATH Path to private key (default mkit internal)
--ssl-cert-file PATH Path to certificate (default mkit internal)
```

There's also samples for [systemd](samples/systemd) and [daemontools](samples/daemontools) as well for some miscellaneous [spps](samples/apps).

### Accessing the API

A client is provided to interact with MKIt server.

Run `mkitc help` for a list of current supported commands.

```
Usage: mkitc <command> [options]
Micro k8s on Ruby - a simple tool to mimic a (very) minimalistic k8 cluster
Commands:
init init mkit client
ps show services status (alias for status)
status show services status
logs prints service logs
start start service
stop stop service
restart restart service
create create new service
update update service
rm remove service
version prints mkit server version
proxy haproxy status and control
profile mkit client configuration profile
Run 'mkitc help <command>' for specific command information.
```

Example:

```
$ mkitc ps
+----+-------+---------------+-------------------+--------------+---------+
| id | name | addr | ports | pods | status |
+----+-------+---------------+-------------------+--------------+---------+
| 1 | mongo | 10.210.198.10 | tcp/27017 | 106e2b59cb11 | RUNNING |
| 2 | nexus | 10.210.198.11 | http/80,https/443 | 68e239e5102a | RUNNING |
+----+-------+---------------+-------------------+--------------+---------+
```
The service `mongo` is available on IP `10.210.198.10:27017`
The service `nexus` is available on IP `10.210.198.11:80` and on port `443` with ssl.

**Note:** Don't forget to call `mkitc init` to initialize client configuration and to add the `client-id`
to the server authorized clients list.

## Development

* build the gem
Expand Down
36 changes: 30 additions & 6 deletions bin/mkitc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class CommandPalette
{ short: '-v', long: '--verbose', help: 'verbose', mandatory: false, value: nil }
]
[
{
cmd: 'init',
help: 'init mkit client',
request: { }
},
{
cmd: 'ps',
args: [
Expand Down Expand Up @@ -170,6 +175,7 @@ class MKItClient
@config_dir = "#{ENV['HOME']}/.mkit"
@profile_file = "#{@config_dir}/current"
@commands = CommandPalette.new
@config_file = "#{@config_dir}/mkitc_config.yml"
create_default_config
end

Expand All @@ -178,28 +184,34 @@ class MKItClient
puts "Creating config directory on '#{@config_dir}'..."
FileUtils.mkdir_p(@config_dir)
end
FileUtils.cp("#{@root}/config/mkitc_config.yml", @config_dir) unless File.exist?("#{@config_dir}/mkitc_config.yml")
FileUtils.cp("#{@root}/config/mkitc_config.yml", @config_dir) unless File.exist?(@config_file)
profile({ verb: 'set' }, { profile_name: 'local' }) unless File.exist?(@profile_file)
end

def read_configuration
def read_configuration(init_call = false)
current_profile = File.read(@profile_file)
if current_profile.nil? || current_profile.empty?
# force set default
profile({ verb: 'set' }, { profile_name: 'local' })
current_profile = 'local'
end
cfg = YAML.load_file("#{@config_dir}/mkitc_config.yml")
cfg = YAML.load_file(@config_file)

if cfg['mkit'].nil? || cfg['mkit'][current_profile.lstrip].nil?
raise InvalidParametersException, "invalid configuration found on '~/.mkit' or profile not found"
end

@configuration = cfg['mkit'][current_profile.lstrip]
if !init_call && cfg['my_id'].nil?
raise InvalidParametersException.new("Please run 'mkitc init' to initialize mkit client.", find_command('init'))
end
@my_id = cfg['my_id']
cfg
end

def client
def client(req)
read_configuration
req['X-API-KEY'] = @my_id
uri = URI(@configuration['server.uri'])
case uri.scheme
when 'https'
Expand All @@ -213,7 +225,7 @@ class MKItClient
else
raise InvalidParametersException, 'Invalid mkit server uri. Please check configuration'
end
@client
@client.request(req)
end

def dict
Expand Down Expand Up @@ -317,7 +329,7 @@ class MKItClient
when :delete
req = Net::HTTP::Delete.new(uri)
end
client.request(req).body
client(req).body
end

def attach(file)
Expand Down Expand Up @@ -371,6 +383,18 @@ class MKItClient
exit 1
end

def init(request, request_hash = nil)
cfg = read_configuration(true)
if cfg['my_id'].nil?
my_id = SecureRandom.uuid.gsub('-','')
cfg['my_id'] = my_id
File.write(@config_file, cfg.to_yaml)
puts "Please check if your api-key is on mkitd server allowed keys"
else
my_id = cfg['my_id']
end
puts "Your api-key is #{my_id}"
end
def create(request, request_hash = nil)
unless File.file?(request_hash[:file])
raise InvalidParametersException.new('File not found.', find_command('create'))
Expand Down

0 comments on commit dc01d25

Please sign in to comment.