twproxy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d76c0ff8f096bc96a9a2bb037879a31fd08f958f
4
+ data.tar.gz: e56c25cac077859244f4e062af7edcc59df27b4c
5
+ SHA512:
6
+ metadata.gz: fe6b63a884f567df715720db6b1ee556b91ae947e0e22def49b3911c70a2dee0851054a73478e8392a45fc3785ac8e5db466968101bb09afb3f8c48c7276c08e
7
+ data.tar.gz: ba4b836a56c8316719a53a456e6bf59c3f468e681337e7289e4aae42fec1254a950def4d3d1b7dbb65ccacf10950ecc35b50778e59336be47ab37607a9f38a4e
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sinatra"
4
+ gem "haml"
5
+ gem "rotp"
6
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2016 Steve Gattuso
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # TiddlyWiki Proxy
2
+ *An authenticated proxy for protecting your wiki.*
3
+
4
+ ![Screencast](http://i.imgur.com/NmGf4iE.gif)
5
+
6
+ This gem provides an easy way to secure a TiddlyWiki installation so you can
7
+ expose it to the internet without having to worry about others gaining access.
8
+ The proxy provides you with a username, password and optionally a 2-factor auth
9
+ code (based on TOTP, which is compatible with [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en)).
10
+
11
+ Although this proxy can be used as a server in itself, it's highly recommended
12
+ that you run it behind a proper webserver (such as nginx's reverse proxy) that
13
+ is secured with SSL.
14
+
15
+ ## Usage reference
16
+ Installation:
17
+ ```
18
+ $ gem install twproxy
19
+ ```
20
+
21
+ Argument reference:
22
+ ```
23
+ $ twproxy --help
24
+ Usage: twproxy [options]
25
+ -p, --port PORT Specify port to run the proxy on. Defaults to 8888.
26
+ -b, --bind HOSTNAME Specify hostname to bind to. Defaults to 127.0.0.1.
27
+ -s, --enable-ssl Ensures cookies are marked as secure.
28
+ -d, --destination URL Specify the url of the TiddlyWiki server. Defaults to http://localhost:8080.
29
+ -g CLEARTEXT, Generates a SHA1 hashed password
30
+ --generate-password
31
+ -u, --username USER Sets the username. Defaults to user.
32
+ -P, --password HASHED Sets the user's password. Use a SHA1 hash or -g. Defaults to test.
33
+ -a, --auth KEY Sets a TOTP key for use with Google Authenticator.
34
+ -h, --help Displays this help
35
+ ```
36
+
37
+ ## Basic Usage
38
+ Let's say we have a
39
+ [tiddlywiki server](http://tiddlywiki.com/static/Using%2520TiddlyWiki%2520on%2520Node.js.html)
40
+ running on `http://localhost:8080` that we'd like to protect with twproxy.
41
+ Fistly we'll want to generate a hashed version of the password we'd like to use:
42
+
43
+ ```
44
+ $ twproxy -g helloWorld
45
+ 5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a
46
+ ```
47
+
48
+ Great! Now let's spin up twproxy so we can start working on our wiki,
49
+ protecting it with a username of `stevenleeg` and a password of `helloWorld`:
50
+
51
+ ```
52
+ $ twproxy -u stevenleeg -P 5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a -d http://localhost:8080/
53
+ ```
54
+
55
+ If all goes well you can now navigate to `http://localhost:8888` and see a
56
+ prompt for your username and password.
57
+
58
+ That's all there is to it! Twproxy is set up and ready to go for basic usage,
59
+ however if you're paranoid I'd recommend further securing your wiki with SSL
60
+ (using an nginx reverse proxy) and 2FA.
61
+
62
+ ## Enabling 2FA
63
+ Sometimes a username and password isn't enough for paranoid minds. Not to
64
+ worry! Twproxy lets you add another layer of security onto your wiki by
65
+ supporting time-based one-time password authentication. This allows you to also
66
+ require a unique one-time code from Google Authenticator (or any other TOTP app)
67
+ in order to sign in.
68
+
69
+ Setting up 2FA requires acquiring a secret key that is used to generate one-time
70
+ passwords. [This online generator](http://www.xanxys.net/totp/) will do the job,
71
+ but if you're serious about security you should know never to trust an online
72
+ generator of secret keys.
73
+
74
+ For this example use case I used the online generator to get a key (note, I
75
+ copied the base32 version of the secret key):
76
+ `o76swdmjl74izl7bihmziod333cwnje3`.
77
+
78
+ I can then add 2FA to our previous example by adding the `-a` flag:
79
+ ```
80
+ $ twproxy -u stevenleeg -P 5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a -d http://localhost:8080/ -a o76swdmjl74izl7bihmziod333cwnje3
81
+ ```
82
+
83
+ To test it, scan the QR code on the secret key generator's site into your
84
+ favorite TOTP app and try logging in (the authentication screen will now show a
85
+ third box for an auth code).
86
+
87
+ If all goes well you should only be able to sign in after entering the correct
88
+ auth code generated on your phone.
89
+
90
+ ## Enabling SSL
91
+ If you plan on exposing your wiki to the internet, it's a smart idea to protect
92
+ it by running it over an encrypted HTTPS connection. The easiest way to do this
93
+ is by using an [nginx reverse proxy](https://www.nginx.com/resources/admin-guide/reverse-proxy/)
94
+ paired with an SSL certificate from [Let's Encrypt](https://letsencrypt.org/).
95
+
96
+ Getting those two set up is out of scope of this readme, however it may be
97
+ helpful to check out DigitalOcean's [SSL installation guide](https://www.digitalocean.com/community/tutorials/how-to-create-an-ssl-certificate-on-nginx-for-ubuntu-14-04) if you're lost.
98
+
99
+ An important note for those attempting to run twproxy over an SSL connection:
100
+ you must add the `--enable-ssl` flag when starting the proxy! Without this flag twproxy
101
+ won't mark cookies as secure, leaving you confused each time you log in
102
+ correctly only to see yet another authentication prompt.
103
+
104
+ ## Using with Docker
105
+ **Note:** More documentation on this to come. This is still a WIP.
106
+
107
+ ## Issues
108
+ If you're having trouble with twproxy please don't hesitate to open an issue to
109
+ let me know. I'm actively maintaining this project for my own use, so it's in
110
+ my best interest to make sure it is secure and functional.
111
+
112
+ If you are filing a bug report please make sure you describe all steps
113
+ necessary to reproduce the issue. This will make my job much easier and make
114
+ sure your bug gets fixed faster.
115
+
116
+ Happy wiki-ing!
117
+
data/bin/twproxy ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ require "twproxy"
3
+ require "sinatra"
4
+ require "optparse"
5
+ require "digest/sha1"
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: twproxy [options]"
10
+
11
+ opts.on("-p", "--port PORT", "Specify port to run the proxy on. Defaults to 8888.") do |port|
12
+ options[:port] = port
13
+ end
14
+ opts.on("-b", "--bind HOSTNAME", "Specify hostname to bind to. Defaults to 127.0.0.1.") do |hostname|
15
+ options[:bind] = hostname
16
+ end
17
+ opts.on("-s", "--enable-ssl", "Ensures cookies are marked as secure.") do |ssl|
18
+ options[:enable_ssl] = ssl
19
+ end
20
+ opts.on("-d", "--destination URL", "Specify the url of the TiddlyWiki server. Defaults to http://localhost:8080.") do |url|
21
+ options[:url] = url
22
+ end
23
+ opts.on("-g", "--generate-password CLEARTEXT", "Generates a SHA1 hashed password") do |pass|
24
+ puts Digest::SHA1.hexdigest(pass)
25
+ exit
26
+ end
27
+ opts.on("-u", "--username USER", "Sets the username. Defaults to user.") do |user|
28
+ options[:username] = user
29
+ end
30
+ opts.on("-P", "--password HASHED", "Sets the user's password. Use a SHA1 hash or -g. Defaults to test.") do |hash|
31
+ options[:password] = hash
32
+ end
33
+ opts.on("-a", "--auth KEY", "Sets a TOTP key for use with Google Authenticator.") do |key|
34
+ options[:auth] = key
35
+ end
36
+ opts.on("-h", "--help", "Displays this help") do
37
+ puts opts
38
+ exit
39
+ end
40
+ end.parse!
41
+
42
+ TWProxy.set :bind, options[:bind] || "127.0.0.1"
43
+
44
+ TWProxy.set :port, options[:port] || 8888
45
+ TWProxy.set :enable_ssl, options[:enable_ssl] || false
46
+ TWProxy.set :url, options[:url] || "http://localhost:8080" || ENV['WIKI_URL']
47
+ TWProxy.set :username, options[:username] || ENV['WIKI_USER'] || "user"
48
+ # Default password is test
49
+ TWProxy.set :password,
50
+ options[:password] || ENV['WIKI_PASS'] || "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
51
+ TWProxy.set :auth, options[:auth] || ENV['WIKI_AUTH']
52
+
53
+ TWProxy.run!
54
+
@@ -0,0 +1,115 @@
1
+ require "sinatra"
2
+ require "net/http"
3
+ require "uri"
4
+ require "digest/sha1"
5
+ require "rotp"
6
+
7
+ class TWProxy < Sinatra::Base
8
+ ##
9
+ # Authenticate each request before permitting it to access the wiki
10
+ #
11
+ before do
12
+ @accepted_token = Digest::SHA1.hexdigest(
13
+ "#{settings.username}#{settings.password}#{settings.auth}"
14
+ )
15
+
16
+ token = request.cookies["auth"]
17
+
18
+ if token == @accepted_token
19
+ @authenticated = true
20
+ elsif request.path != "/login"
21
+ redirect "/login"
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Authentication handlers
27
+ #
28
+ get "/login" do
29
+ redirect "/" if @authenticated
30
+ haml :login
31
+ end
32
+
33
+ post "/login" do
34
+ hashed_pass = Digest::SHA1.hexdigest(params["pass"])
35
+ user = params["user"] == settings.username
36
+ pass = hashed_pass == settings.password
37
+
38
+ if settings.auth
39
+ totp = ROTP::TOTP.new(settings.auth)
40
+ auth = totp.verify(params["auth"])
41
+ else
42
+ auth = true
43
+ end
44
+
45
+ if !auth
46
+ @error = "Auth code is incorrect"
47
+ haml :login
48
+ elsif user && pass && auth
49
+ token = Digest::SHA1.hexdigest(
50
+ "#{params["user"]}#{hashed_pass}#{settings.auth}"
51
+ )
52
+ response.set_cookie("auth", value: token,
53
+ secure: settings.enable_ssl,
54
+ expires: (Date.today >> 1).to_time,
55
+ httponly: true)
56
+
57
+ redirect "/"
58
+ else
59
+ @error = "Invalid username/password"
60
+ haml :login
61
+ end
62
+ end
63
+
64
+ get "/logout" do
65
+ response.set_cookie("auth", nil)
66
+ redirect "/login"
67
+ end
68
+
69
+ ##
70
+ # Wiki proxy
71
+ #
72
+ def get_uri
73
+ capture = URI::decode(params[:captures][0])
74
+ base = settings.url
75
+ if settings.url[-1] != '/'
76
+ base += '/'
77
+ end
78
+
79
+ uri = URI.parse("#{base}#{URI::encode(capture)}")
80
+ end
81
+
82
+ put /\/(.*)/ do
83
+ uri = get_uri
84
+ http = Net::HTTP.new(uri.host, uri.port)
85
+
86
+ req = Net::HTTP::Put.new(uri.request_uri, initheader = { "Content-Type" => "application/json"})
87
+ request.body.rewind
88
+ req.body = request.body.read
89
+
90
+ resp = http.request(req)
91
+
92
+ status resp.code
93
+ headers({"Etag" => resp["Etag"]})
94
+ resp.body
95
+ end
96
+
97
+ delete /\/(.*)/ do
98
+ uri = get_uri
99
+ http = Net::HTTP.new(uri.host, uri.port)
100
+
101
+ req = Net::HTTP::Delete.new(uri.request_uri)
102
+ resp = http.request(req)
103
+
104
+ status resp.code
105
+ resp.body
106
+ end
107
+
108
+ get /\/(.*)/ do
109
+ uri = get_uri
110
+ http = Net::HTTP.new(uri.host, uri.port)
111
+ req = Net::HTTP::Get.new(uri.request_uri)
112
+
113
+ http.request(req).body
114
+ end
115
+ end
@@ -0,0 +1,56 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title wiki - login
5
+ :css
6
+ body {
7
+ font-family: 'Helvetica';
8
+ color: #111;
9
+ font-weight: 300;
10
+ }
11
+ .box {
12
+ width: 400px;
13
+ position: fixed;
14
+ top: 50%;
15
+ left: 50%;
16
+ transform: translate(-50%, -80%);
17
+ background: #EEE;
18
+ padding: 15px;
19
+ box-sizing: border-box;
20
+ }
21
+ h1 {
22
+ margin: 0px 0px 10px 0px;
23
+ font-weight: 300;
24
+ font-size: 24px;
25
+ }
26
+ input {
27
+ display: block;
28
+ width: 100%;
29
+ margin-bottom: 5px;
30
+ font-family: 'Helvetica';
31
+ font-weight: 300;
32
+ font-size: 18px;
33
+ padding: 5px;
34
+ box-sizing: border-box;
35
+ outline: none;
36
+ }
37
+
38
+ input[type=submit] {
39
+ background: #00B4FF;
40
+ border: 0px;
41
+ color: #FFF;
42
+ margin-bottom: 0px;
43
+ }
44
+
45
+ %body
46
+ .box
47
+ %h1 Login
48
+ - if @error
49
+ %p= @error
50
+
51
+ %form{action:'/login', method: 'POST'}
52
+ %input{type: 'text', placeholder: 'Username', name: 'user'}
53
+ %input{type: 'password', placeholder: 'Password', name: 'pass'}
54
+ - if settings.auth
55
+ %input{type: 'number', placeholder: 'Auth code', name: 'auth'}
56
+ %input{type: 'submit', value: 'Login'}
data/lib/twproxy.rb ADDED
@@ -0,0 +1 @@
1
+ require 'twproxy/proxy'
data/twproxy.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "twproxy"
3
+ s.version = "0.0.1"
4
+ s.date = "2016-04-09"
5
+ s.summary = "TiddlyWiki Proxy"
6
+ s.description = "An authenticated proxy for TiddlyWiki"
7
+ s.authors = ["Steve Gattuso"]
8
+ s.email = "steve@stevegattuso.me"
9
+ s.files = ["lib/twproxy.rb"]
10
+ s.files = %w(Gemfile LICENSE README.md twproxy.gemspec) + Dir['lib/**/*', 'bin/*']
11
+ s.license = "MIT"
12
+ s.executables << 'twproxy'
13
+
14
+ s.add_dependency 'sinatra', '~> 1.4'
15
+ s.add_dependency 'haml', '~> 4.0'
16
+ s.add_dependency 'rotp', '~> 2.1'
17
+ end
18
+
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twproxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Steve Gattuso
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: haml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rotp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
55
+ description: An authenticated proxy for TiddlyWiki
56
+ email: steve@stevegattuso.me
57
+ executables:
58
+ - twproxy
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - Gemfile
63
+ - LICENSE
64
+ - README.md
65
+ - bin/twproxy
66
+ - lib/twproxy.rb
67
+ - lib/twproxy/proxy.rb
68
+ - lib/twproxy/views/login.haml
69
+ - twproxy.gemspec
70
+ homepage:
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.6.3
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: TiddlyWiki Proxy
94
+ test_files: []