Compare commits
8 Commits
cca1aa4604
...
c66d4676e3
Author | SHA1 | Date | |
---|---|---|---|
c66d4676e3
|
|||
a93cab4de5
|
|||
b2e4fcbce1
|
|||
86ba2e6c1a
|
|||
3a78bf5d03
|
|||
fde1dd14b5
|
|||
a4955d35fa
|
|||
aa907dfa5f
|
@ -69,4 +69,4 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: git.kill0.net/ryanc/kipunji:latest
|
||||
tags: git.kill0.net/ryanc/kubernaut:latest
|
||||
|
113
app.rb
113
app.rb
@ -13,9 +13,14 @@ require "anyflake"
|
||||
|
||||
require "jwt"
|
||||
|
||||
SESSION_SECRET_HEX_LENGTH = 64
|
||||
$LOAD_PATH.unshift File.dirname(__FILE__) + "/lib"
|
||||
|
||||
set :session_secret, ENV.fetch("SESSION_SECRET") { SecureRandom.hex(SESSION_SECRET_HEX_LENGTH) }
|
||||
require "config"
|
||||
|
||||
SESSION_SECRET_HEX_LENGTH = 64
|
||||
JWT_SECRET_HEX_LENGTH = 64
|
||||
|
||||
ENV_PREFIX = "KUBERNAUT"
|
||||
|
||||
CLK_TCK = 100
|
||||
PID_FILE_PATH = "/run/app/pid".freeze
|
||||
@ -38,7 +43,9 @@ DURATION_PARTS = [
|
||||
[1, "second", "s"]
|
||||
].freeze
|
||||
|
||||
JWT_SECRET = SecureRandom.bytes(64).freeze
|
||||
config = Config.new
|
||||
|
||||
set :session_secret, config.session_secret.unwrap
|
||||
|
||||
module Sinatra
|
||||
module RequestHeadersHelper
|
||||
@ -209,12 +216,13 @@ before do
|
||||
end
|
||||
|
||||
helpers do
|
||||
def json(obj, opts: nil, pretty: false)
|
||||
if pretty
|
||||
def jsonify(obj, opts: nil, pretty: false)
|
||||
buf = if pretty
|
||||
JSON.pretty_generate obj, opts:
|
||||
else
|
||||
JSON.generate(obj, opts:)
|
||||
end
|
||||
"#{buf}\n"
|
||||
end
|
||||
|
||||
def protected! hidden = false
|
||||
@ -244,14 +252,14 @@ end
|
||||
get "/env", provides: "json" do
|
||||
pretty = params.key? :pretty
|
||||
|
||||
json ENV.sort.to_h, pretty:
|
||||
jsonify ENV.sort.to_h, pretty:
|
||||
end
|
||||
|
||||
get "/headers", provides: "json" do
|
||||
pretty = params.key? :pretty
|
||||
h = req_headers
|
||||
|
||||
json h, pretty:
|
||||
jsonify h, pretty:
|
||||
end
|
||||
|
||||
get "/livez" do
|
||||
@ -266,7 +274,7 @@ get "/livez/uptime" do
|
||||
tt = TickTock.new
|
||||
x = {started_at: tt.started_at, seconds: tt.uptime.to_i, human: human_time(tt.uptime.to_i)}
|
||||
|
||||
json x
|
||||
jsonify x
|
||||
end
|
||||
|
||||
post "/livez/toggle" do
|
||||
@ -330,7 +338,7 @@ end
|
||||
get "/pid" do
|
||||
pretty = params.key? :pretty
|
||||
|
||||
json({puma: master_pid, pid: Process.pid}, pretty:)
|
||||
jsonify({puma: master_pid, pid: Process.pid}, pretty:)
|
||||
end
|
||||
|
||||
get "/token" do
|
||||
@ -340,31 +348,37 @@ get "/token" do
|
||||
token = JWT.encode payload, JWT_SECRET, "HS256"
|
||||
x = {token: token, expires_at: expires_at}
|
||||
|
||||
json x
|
||||
jsonify x
|
||||
end
|
||||
|
||||
get "/token/validate" do
|
||||
token = req_headers["authorization"].split[1]
|
||||
payload = JWT.decode token, JWT_SECRET, true, algorithm: "HS256"
|
||||
|
||||
json payload
|
||||
jsonify payload
|
||||
end
|
||||
|
||||
post "/session" do
|
||||
session.merge! params
|
||||
|
||||
json session.to_hash
|
||||
jsonify session.to_hash
|
||||
end
|
||||
|
||||
get "/session" do
|
||||
j = session.to_hash
|
||||
j[:hostname] = ENV["HOSTNAME"]
|
||||
|
||||
json j
|
||||
jsonify j
|
||||
end
|
||||
|
||||
get "/cookies" do
|
||||
json response.headers
|
||||
jsonify response.headers
|
||||
end
|
||||
|
||||
get "/config", provides: "json" do
|
||||
pretty = params.key? :pretty
|
||||
|
||||
jsonify config.as_json, pretty:
|
||||
end
|
||||
|
||||
get "/_cat/headers" do
|
||||
@ -391,6 +405,21 @@ get "/_cat/cookies" do
|
||||
end
|
||||
end
|
||||
|
||||
get "/_cat/config" do
|
||||
stream do |out|
|
||||
config.instance_variables.each do |k|
|
||||
k = k.to_s.delete_prefix "@"
|
||||
begin
|
||||
v = config.send(k)
|
||||
rescue NoMethodError
|
||||
next
|
||||
end
|
||||
|
||||
out << "#{k}(#{v.to_s.length})=#{v}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
route :delete, :get, :patch, :post, :put, "/status/:code" do
|
||||
# hello
|
||||
code = Integer(params[:code])
|
||||
@ -398,11 +427,14 @@ route :delete, :get, :patch, :post, :put, "/status/:code" do
|
||||
end
|
||||
|
||||
get "/chunked/:delay" do
|
||||
content_type "application/x-ndjson"
|
||||
|
||||
delay = Float(params[:delay])
|
||||
stream do |out|
|
||||
out << "Hello, world!\n"
|
||||
sleep delay
|
||||
out << "Hello, world!\n"
|
||||
30.times do |i|
|
||||
out << jsonify({id: i, message: i % 2 == 0 ? "tick" : "tock"})
|
||||
sleep delay
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -415,5 +447,50 @@ route :delete, :get, :patch, :post, :put, "/auth/basic", provides: "json" do
|
||||
protected!
|
||||
end
|
||||
|
||||
json({authenticated: true, user: @auth.username}, pretty:)
|
||||
jsonify({authenticated: true, user: @auth.username}, pretty:)
|
||||
end
|
||||
|
||||
def human_size_to_bytes(size)
|
||||
units = %i[b kb mb gb tb pb eb zb yb rb qb]
|
||||
|
||||
number, unit = size.split(/(?<=\d)(?=[A-Za-z])/)
|
||||
|
||||
raise ArgumentError, "the unit is not recognized" if unit.nil?
|
||||
|
||||
number = Float(number)
|
||||
unit = unit.downcase.to_sym
|
||||
|
||||
exponent = units.find_index(unit)
|
||||
|
||||
number *= (1024**exponent)
|
||||
Integer(number.ceil)
|
||||
end
|
||||
|
||||
MAX_DOWNLOAD_SIZE = "1GB"
|
||||
|
||||
get "/bytes/:size" do
|
||||
size = params[:size]
|
||||
n = [human_size_to_bytes(size), human_size_to_bytes(MAX_DOWNLOAD_SIZE)].min
|
||||
|
||||
headers["content-type"] = "application/octet-stream"
|
||||
headers["content-length"] = n
|
||||
|
||||
headers["content-disposition"] = "attachment; filename=\"#{params[:f]}\"" if params.key? :f
|
||||
|
||||
def generate_bytes(number, byte = "\x00", block_size = 4096)
|
||||
raise ArgumentError, "'byte' must be 1 byte" unless byte.b.length == 1
|
||||
|
||||
bytes_written = 0
|
||||
block = byte * block_size
|
||||
Enumerator.new do |g|
|
||||
while bytes_written < number
|
||||
remaining_bytes = number - bytes_written
|
||||
bytes_to_write = [block_size, remaining_bytes].min
|
||||
g.yield block[0, bytes_to_write]
|
||||
bytes_written += bytes_to_write
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
generate_bytes(Integer(n))
|
||||
end
|
||||
|
@ -2,7 +2,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: kipunji-configmap
|
||||
namespace: kipunji
|
||||
name: kubernaut-configmap
|
||||
namespace: kubernaut
|
||||
data:
|
||||
KIPUNJI_CAT: kilwin
|
||||
KUBERNAUT_CAT: kilwin
|
||||
|
@ -2,22 +2,22 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kipunji
|
||||
name: kubernaut
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
spec:
|
||||
replicas: 5
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kipunji
|
||||
app: kubernaut
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kipunji
|
||||
app: kubernaut
|
||||
spec:
|
||||
containers:
|
||||
- name: kipunji
|
||||
image: git.kill0.net/ryanc/kipunji:latest
|
||||
- name: kubernaut
|
||||
image: git.kill0.net/ryanc/kubernaut:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: sinatra-web
|
||||
@ -26,12 +26,12 @@ spec:
|
||||
- name: SESSION_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kipunji-session-secret
|
||||
name: kubernaut-session-secret
|
||||
key: session_secret
|
||||
optional: true
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: kipunji-configmap
|
||||
name: kubernaut-configmap
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: kipunji
|
||||
namespace: kubernaut
|
||||
resources:
|
||||
- secret.yaml
|
||||
- configmap.yaml
|
||||
|
@ -3,13 +3,13 @@ apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: kipunji-session-secret
|
||||
namespace: kipunji
|
||||
name: kubernaut-session-secret
|
||||
namespace: kubernaut
|
||||
spec:
|
||||
encryptedData:
|
||||
session_secret: AgCY08t0AU418znEZt5d252J+lH+fwYki2g6jdJpfdRfVQjnA+b52P0KWrs/x5pB0PKab6Z3JY/Tz0SQCaoIsCR4IzUO3a095aulRqb6Qr1Lz8udBVta4JJMZLmo26tuUfVHlpD1d6J8rkBSm8vzckFLkOA1Wfl/9rS3K4qwiDogA5pI0ULghFkeEx1yKdRwPq0k8PuvOvLUJ6oNq3e5n+B/BrVWdQ+7XQxUq/AMANJrDbe+RD33f99LArHYA7bFMbY8YRazXSTAkeunpTlxTjuGZKYvJKupo29LHz2OVbZVX/hI0nZkdVpcgqvbxF6Vw9CuCeAmtKYl7A3qsAWqDLUdP3hRLsk2P9RDNhEzYWh4ml8APzziWzihdJbGEjwLy7HsHgKslM0XbBnRQDlxp/JtvcWdjQp33A+QOON32zOKHi+qJjDYyGebS1+xkPbnyb1MPSJVAtFpj7dlLbFekLFDZEbXuJYUl1wKdFOIjJHmNK/MTEV2kOhtiVj/aeKgSXwor9hR7Uxzs5ZSawp9uWw+hpr58EX6I+RtfO4yjFC6FjnagiU6SlI1Q2F7/nv82g1UWTYMpNN5bduS1YFWmsnXvK+W7YQHpSForr5ndtCSHmclbXb5Fc33sywC5u6Bi2Gu5/MW6d73BOog5BC3QtOuEQ044Q+cuU3RIlKADBqKLzZmHlmukyyGuZfXJnGjlWGKp3J1KecucTo6XC9QHpUkjXEKdlE63mOI1VuOGyBIHl60v4bnWiBg+aDZVHipz4JLKsVB0HOgBBK7+tOX6tr1GDG/F7Nz/i9ebzUV6i8Ec1jHf+2ZcTtBkNXBIkHc84+4Qd33/gOuP+lizLfIhfQ3DFWbwyfYumpVbeapyYhB0CE=
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: kipunji-session-secret
|
||||
namespace: kipunji
|
||||
name: kubernaut-session-secret
|
||||
namespace: kubernaut
|
||||
|
@ -2,11 +2,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kipunji
|
||||
name: kubernaut
|
||||
spec:
|
||||
ports:
|
||||
- name: web
|
||||
port: 80
|
||||
targetPort: sinatra-web
|
||||
selector:
|
||||
app: kipunji
|
||||
app: kubernaut
|
||||
|
@ -2,8 +2,7 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: kipunji
|
||||
namespace: kipunji
|
||||
name: kubernaut
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- ./app
|
||||
|
@ -2,19 +2,19 @@
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: kipunji-memcached
|
||||
name: kubernaut-memcached
|
||||
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kipunji-memcached
|
||||
app: kubernaut-memcached
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kipunji-memcached
|
||||
app: kubernaut-memcached
|
||||
spec:
|
||||
containers:
|
||||
- name: kipunji-memcached
|
||||
- name: kubernaut-memcached
|
||||
image: memcached:latest
|
||||
ports:
|
||||
- name: memcached
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: kipunji
|
||||
namespace: kubernaut
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- services.yaml
|
||||
|
@ -2,7 +2,7 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kipunji-memcached
|
||||
name: kubernaut-memcached
|
||||
|
||||
spec:
|
||||
ports:
|
||||
@ -10,4 +10,4 @@ spec:
|
||||
port: 11211
|
||||
targetPort: memcached
|
||||
selector:
|
||||
app: kipunji-memcached
|
||||
app: kubernaut-memcached
|
||||
|
@ -2,6 +2,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kipunji
|
||||
name: kubernaut
|
||||
labels: {}
|
||||
|
||||
|
50
lib/config.rb
Normal file
50
lib/config.rb
Normal file
@ -0,0 +1,50 @@
|
||||
require "sensitive"
|
||||
|
||||
class Config
|
||||
attr_accessor :cat
|
||||
|
||||
attr_reader :jwt_secret, :session_secret
|
||||
|
||||
def initialize(prefix = ENV_PREFIX, jwt_secret = nil, session_secret = nil, cat = nil)
|
||||
@prefix = prefix
|
||||
@cat = cat
|
||||
|
||||
session_secret ||= ENV.fetch "SESSION_SECRET" do
|
||||
SecureRandom.hex SESSION_SECRET_HEX_LENGTH
|
||||
end
|
||||
|
||||
jwt_secret ||= fetch_env "JWT_SECRET" do
|
||||
SecureRandom.hex JWT_SECRET_HEX_LENGTH
|
||||
end
|
||||
|
||||
@session_secret = Sensitive.new session_secret
|
||||
@jwt_secret = Sensitive.new jwt_secret
|
||||
@cat ||= ENV.fetch "#{@prefix}_CAT", nil
|
||||
end
|
||||
|
||||
def fetch_env(name, &)
|
||||
ENV.fetch "#{@prefix}_#{name}", &
|
||||
end
|
||||
|
||||
def as_json(options = nil)
|
||||
{jwt_secret: jwt_secret, session_secret: @session_secret, cat: @cat}
|
||||
end
|
||||
|
||||
def to_json(options = nil)
|
||||
if options &&
|
||||
options.key?(:pretty) &&
|
||||
options[:pretty] == true
|
||||
JSON.pretty_generate as_json(options)
|
||||
else
|
||||
JSON.generate as_json(options)
|
||||
end
|
||||
end
|
||||
|
||||
def session_secret=(v)
|
||||
@session_secret = Sensitive.new v
|
||||
end
|
||||
|
||||
def jwt_secret=(v)
|
||||
@jwt_secret = Sensitive.new v
|
||||
end
|
||||
end
|
39
lib/sensitive.rb
Normal file
39
lib/sensitive.rb
Normal file
@ -0,0 +1,39 @@
|
||||
class Sensitive
|
||||
alias_method :eql?, :==
|
||||
alias_method :equal?, :==
|
||||
|
||||
def initialize(v, ch: "*", head: 2, tail: 2)
|
||||
@v = v
|
||||
@ch = ch
|
||||
@head = head
|
||||
@tail = tail
|
||||
end
|
||||
|
||||
def mask(v)
|
||||
"".concat(v[0, @head], @ch * (v.length - (@head + @tail)), v[-@tail, @tail])
|
||||
end
|
||||
|
||||
def unwrap
|
||||
@v
|
||||
end
|
||||
|
||||
def length
|
||||
@v.length
|
||||
end
|
||||
|
||||
def to_s
|
||||
mask @v
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name} @v=#{wrap}>"
|
||||
end
|
||||
|
||||
def hash
|
||||
@v.hash
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(Sensitive) && other.hash == hash
|
||||
end
|
||||
end
|
26
spec/sensitive_spec.rb
Normal file
26
spec/sensitive_spec.rb
Normal file
@ -0,0 +1,26 @@
|
||||
require "minitest/autorun"
|
||||
|
||||
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
||||
|
||||
require "sensitive"
|
||||
|
||||
ALPHABET = ('a' .. 'z').reduce(:concat)
|
||||
|
||||
describe "Sensitive" do
|
||||
before do
|
||||
@s = Sensitive.new ALPHABET
|
||||
end
|
||||
|
||||
it "test initialize" do
|
||||
_(@s.to_s).must_equal "ab" + "*" * 22 + "yz"
|
||||
end
|
||||
|
||||
it "test initialize" do
|
||||
_(@s.unwrap).must_equal ALPHABET
|
||||
end
|
||||
|
||||
it "test using different mask character" do
|
||||
s = Sensitive.new ALPHABET, ch: "x"
|
||||
_(s.to_s).must_equal "x"
|
||||
end
|
||||
end
|
26
test/test_sensitive.rb
Normal file
26
test/test_sensitive.rb
Normal file
@ -0,0 +1,26 @@
|
||||
require "minitest/autorun"
|
||||
|
||||
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
||||
|
||||
require "sensitive"
|
||||
|
||||
ALPHABET = ("a".."z").reduce(:concat)
|
||||
|
||||
class TestSensitive < Minitest::Test
|
||||
def setup
|
||||
@s = Sensitive.new ALPHABET
|
||||
end
|
||||
|
||||
def test_initialize
|
||||
assert_equal @s.to_s, "ab" + "*" * 22 + "yz"
|
||||
end
|
||||
|
||||
def test_unwrap
|
||||
assert_equal @s.unwrap, ALPHABET
|
||||
end
|
||||
|
||||
def test_using_a_different_mask_character
|
||||
s = Sensitive.new ALPHABET, ch: "x"
|
||||
assert_equal s.to_s, "ab" + "x" * 22 + "yz"
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user