Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedding GateOne guideline #119

Closed
aryamazaheri opened this issue Aug 15, 2012 · 34 comments
Closed

Embedding GateOne guideline #119

aryamazaheri opened this issue Aug 15, 2012 · 34 comments

Comments

@aryamazaheri
Copy link

I tried to embed GateOne terminal in a simple html page to see how it is gonna work. It seems that you're currently working on writing a documentation about embedding GateOne in a web app. I read the first part although it's incomplete. I included the gateone.js to my page and created a div with 'gateone' id. But it doesn't show anything. After a while an alert box come up saying that:
You will now be directed to a page where you can accept the Gate One server's SSL certificate.

However, nothing happens.
Is there anything else which I should do in addition to above?

@aryamazaheri
Copy link
Author

To backup my problem, console shows the following error:

ReferenceError: u is not defined
https://localhost/static/gateone.js
Line 1062:
window.location.href = acceptURL + '?url=' + window.location.href.replace(/://(.*@)?/g, '://'+u.randomString(8)+'@');

@aryamazaheri
Copy link
Author

Also, I didn't completely understand what --new_api_key really does. How does it going to help embedding GateOne to our sample webapp?
Could you explain a little bit about this topic?

@aryamazaheri
Copy link
Author

After a little bit thinking about the api key, I am guessing that I should add the api key which gateone.py creates to the user's table in my DB. Then everytime a user wants to access the terminal it calls the GateOne with the respective auth parameters. Am I right?

@liftoff
Copy link
Owner

liftoff commented Aug 15, 2012

The ReferenceError is a bug... I just fixed it (just a typo). That'll be in a commit tonight.

The API key is there to allow your application to pass through it's authenticated user to Gate One. This way Gate One won't have to re-authenticate the user if your application has already taken care of it. The 'chat' app in the tests directory demonstrates how to use the API functionality.

Think of API authentication as a secure form of this:

GateOne.init({'username': 'bob'});

Of course, you can't do that because the user could manipulate the JavaScript on the page to become whatever user they want (hijacking their session, terminals, etc). So you generate a shared secret (API key) between your application and Gate One that you can securely communicate the username with the Gate One server via a little bit of JavaScript (a JSON object, specifically). By generating a signed message every time the user loads your web page it is nearly impossible for an attacker to be able to steal your user's Gate One session.

There's a lot of nuance to secure authentication... If you want to know more about it I recommend you take a look at the code here:

https://github.com/liftoff/GateOne/blob/master/gateone/gateone.py#L1085

It has a lot of comments demonstrating how it to use it and the protections it affords.

@aryamazaheri
Copy link
Author

Thanks. Now it's working without error. But, now it redirects me to the following address:
http://localhost:8888/gateone/static/accept_certificate.html?url=http://xmvtfm6a@localhost:8888/gateone/

I have copied 'accept_certificate.html' to the related folder. It seems that it's working fine but every 5 seconds it redirects the user to the above address and returns back to my page. I think it fails on the certificate.
BTW, I have not passed 'auth' object to my GateOne initialization yet. Could it be the reason? Or am I supposed to do anything else?

Also, I tested the chat sample program. It doesn't work though. Chat program runs on port 8080 but the url it tries to redirect is on 443. So it breaks on redirecting. I changed the default port to 443. But, it didn't work.

@liftoff
Copy link
Owner

liftoff commented Aug 18, 2012

Yeah, there's something strange going on with the code that detects whether or not the client has accepted the server's certificate. I'm working on it.

@liftoff
Copy link
Owner

liftoff commented Aug 18, 2012

I just pushed a commit that should "fix" the redirect issue. I put "fix" in quotes because it is likely that the problem is that your Gate One server is configured for PAM, Google, or Kerberos authentication which won't work with the tutorial (as it exists right now anyway).

So now if you're trying out the tutorial in this situation it will pop-up a message telling you precisely what the problem is.

Also, the commit I just pushed will enable users to resume their sessions when Gate One is embedded into another app with anonymous authentication enabled. Resuming sessions always worked fine when using API authentication but it never worked for anonymous auth (when embedding anyway). So that's that.

Let me know if the aforementioned changes have resolved this problem for you. Thanks

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

I just pushed another commit that now features a fully-working "DIY" section in the tutorial covering "embedded mode". It isn't complete but at least it works as designed now. Please let me know what you think when you get around to checking it out =D

@aryamazaheri
Copy link
Author

Great! With the latest commit the problems have gone away.
I have decided not to use PAM authentication and try to rely on my portal authentication backend. Because it's silly to re-authenticate users for using GateOne. So auth method is set to None in my server.conf file.

I have an opinion about the api key:
When I create an api key with the 'gateony.py --new_api_key', a new key will be placed into my server.conf file and also shown on my terminal. Is there any simple way to get the recent created api key? I need it because everytime a new user signs up in my portal, my app has to create a new api key for him and place it in the DB. But the output is something like this and it's hard to extract it.
[I 120820 01:23:57 gateone:2582] A new API key has been generated: MTA2OGU3ODhjMTRkNGUzY2IwYmE3OWM2ZjJjZGI5ZGMzN
[I 120820 01:23:57 gateone:2583] This key can now be used to embed Gate One into other applications.

Isn't it better to print out api key only?

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

You're misunderstanding how to use the API key... You only need one API key per application. So you run it the --new_api_key once and you're done. From then on when you want to preauthenticate a user and pass that to Gate One you sign the user's information using that API key.

It doesn't matter what user you're signing with your API key. All that matters is that your API key is registered in your server.conf and that the signature is valid. So you could pass-through hundreds of different users with the same API key.

@aryamazaheri
Copy link
Author

Aha! I got it.
But, what's the purpose of API key? Users can easily see this key with some tools like Firebug or anything else. I read the code which is related to authentication and signature of the user. I think the only critical parameter is the timestamp which is checked with a time frame window.
BTW, what is the value of api_time_window?

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

FYI: Here's an example of using the API key to create the 'auth' object in PHP (what language are you using?):

$authobj = array('api_key' => '<your API key>', 'upn' => $_SERVER['REMOTE_USER'], 'timestamp' => time() . '0000', 'signature_method' => 'HMAC-SHA1', 'api_version' => '1.0');
$authobj['signature'] = hash_hmac('sha1', $authobj['api_key'] . $authobj['upn'] . $authobj['timestamp'], '<your API secret>');

In the above example I'm constructing what will end up as the 'auth' object that will be passed to GateOne.init() via the 'authobj' variable. The first line creates the object and the second line creates the signature using the existing object's values. Here's how it could be used to pass through that user's information:

GateOne.init({url:'https://gateone.company.com/', auth:<?php echo json_encode($authobj)?>});

The 'auth' object is just JSON containing the username (aka 'upn') and some other information that's necessary to verify the message.

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

Users will be able to see your API key via the developer tools but they will not be able to see your API secret. The secret is known to your application (on the back-end) and to the Gate One server. The client never gets that information. All they get is a signed JSON message that is only valid within the time frame specified by the api_time_window (so that answers that question too =).

@aryamazaheri
Copy link
Author

Ooh! You're right. I didn't notice the secret key. Thanks for clarifying. :)

@aryamazaheri
Copy link
Author

However, I am still thinking that API key is redundant. We can neglect it and the code will work fine. :)

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

BTW: The reason for the api authentication time window is to prevent authentication replay attacks... Say your application just generated a valid 'auth' object which was used by a client. An attacker could intercept that information and use it to hijack the user's session.

Gate One keeps track of all authentication signatures so that once it has been used it cannot be used again... So that's not a problem unless you restart your Gate One server right after the 'auth' object is generated. In that case when the server comes back up it won't be aware that it has already been used. To prevent this (unlikely event) from happening I added the api_time_window option which essentially amounts to a maximum time window in which an authentication signature is valid.

In other words, as long as you wait longer than api_time_window before restarting your Gate One server you will never have to worry about authentication replay attacks :). It works in a similar fashion to Kerberos, actually.

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

You're right: API-based authentication isn't essential. With "auth = None" users will still be able to do everything they could without API-based authentication except some things will be inconvenient or just won't work:

  • Users won't be able to resume their session from a different browser/computer.
  • All logs will be visible to all users and they'll all appear to be from ANONYMOUS. Of course, if you're not using logging this doesn't matter.
  • All bookmarks will be the same for all users and all users will be able to edit/delete these bookmarks.
  • SSH Identities and the known_hosts file will be shared among all users.

If you're not using ssh_connect.py only the first two bullets will matter. You can remove the SSH, Bookmarks, and Logging plugins too if you don't care about those things. The first bullet will always apply though.

@aryamazaheri
Copy link
Author

So, you're saying that using the API key will resolve all of the above issues?

@liftoff
Copy link
Owner

liftoff commented Aug 19, 2012

Yep! It will ensure that all of your user sessions as associated with their specific identity in your application.

@aryamazaheri
Copy link
Author

Interesting! Thanks a million for such a thorough answer.

@liftoff
Copy link
Owner

liftoff commented Aug 21, 2012

I'm going to close this issue out since everything mentioned has been fixed. Please open a new one if you encounter anything else! Thanks

@liftoff liftoff closed this as completed Aug 21, 2012
@maxking
Copy link

maxking commented Sep 3, 2012

Hi,
I tried embedding GateOne in my web application(RoR) and this error prompts about ssl_certificate not verified. I cloned and installed the repo today itself. Can you help me with this?

@liftoff
Copy link
Owner

liftoff commented Sep 3, 2012

No problem! Just tell me the following:

  • When you say you're getting an "error prompts about ssl_certificate" do you mean a little dialog pops up and you get redirected to the accept_certificate.html page? Or is it the iframe version that tries to accept it without leaving the page?
  • Is your Gate One server using the same certificate as your web app? Is it a purchased certificate or self-signed?
  • Is your Gate One server listening on a different port than your web app?
  • Can you paste the exact error/output from the JS console here so I can see it?
  • Any errors in the Gate One server logs?
  • What browser/OS are you using (probably doesn't matter but you never know)
  • How are you calling GateOne.init() in your app?
  • If possible I'd like to see your app's code... It's usually quicker if I can just have a look at it. I only need to see the HTML/JS relevant to Gate One--not the back-end code.
  • Are you using the latest code from Github or 1.0?

For reference, here's usually what causes "not working (at all)" problems when embedding Gate One...

  • Reverse proxies that don't support WebSockets (e.g. Apache's mod_proxy).
  • Forward proxies that don't support WebSockets (e.g. older versions of popular proxy products and anything that messes with SSL).
  • Calling GateOne.init() with 'embedded:true' but forgetting to call GateOne.Terminal.newTerminal().
  • Forgetting to add the proper origin to the 'origins' setting in server.conf.
  • Running Gate One on a system that doesn't have any CA certificates installed where the Python interpreter can find them (usually embedded systems).

Most other issues are usually bugs or CSS-related :)

@maxking
Copy link

maxking commented Sep 3, 2012

Sorry for now the ssl_error is not popping up but instead there are is some css glitch like you said

  • Yes my GateOne server is using the same certificate as the web app. And its self signed.
  • Yes My GateOne server is running on 443 and web app on 80(reverse proxied using nginx from port 3000)
  • This is the exact error in browser's console http://pastebin.com/zBLxh9yi
  • I am using Chrome 21.0 and Ubuntu 12.04 LTS
  • This is the error from logs, probably i think a CSS thing but i am not able to figure it out.

[W 120903 21:05:10 web:1447] 404 GET /gateone/style?theme=black&container=gateone&prefix=go_ (127.0.0.1) 12.77ms [W 120903 21:05:10 web:1447] 404 GET /gateone/style?colors=default&container=gateone&prefix=go_ (127.0.0.1) 15.41ms

  • My code
  GateOne.init({goURL: "https://localhost/gateone/" ,
  theme: "black",
  fillContainer: false
  });
</script>```

* Yes i cloned the repo today itself and am using the latest code.

@liftoff
Copy link
Owner

liftoff commented Sep 3, 2012

Can you paste your server.conf? I think your problem might have to do with a bad url_prefix setting.

@maxking
Copy link

maxking commented Sep 3, 2012

locale = "en_IN"
pam_service = "login"
syslog_facility = "daemon"
syslog_host = None
enable_unix_socket = True
port = 443
uid = "0"
url_prefix = "/gateone/"
user_dir = "/opt/gateone/users"
dtach = True
certificate = "certificate.pem"
log_to_stderr = False
session_logs_max_age = "30d"
gid = "0"
pid_file = "/var/run/gateone.pid"
sso_realm = None
cookie_secret = "ZTI3YWExZGIzNDFmNGM2Y2EyYzllMTFlNzE4ZDBkMzM3Y"
pam_realm = "phoenix"
sso_service = "HTTP"
https_redirect = False
syslog_session_logging = False
disable_ssl = False
debug = True
session_dir = "/tmp/gateone"
auth = None
address = ""
api_timestamp_window = "30s"
log_file_num_backups = 10
logging = "info"
embedded = False
origins = "http://localhost;https://localhost;http://127.0.0.1;https://127.0.0.1;https://phoenix;https://127.0.1.1"
session_logging = True
unix_socket_path = "/var/run/gateone.sock"
ssl_auth = "none"
log_file_max_size = 104857600
session_timeout = "5d"
command = "/opt/gateone/plugins/ssh/scripts/ssh_connect.py -S '/tmp/gateone/%SESSION%/%SHORT_SOCKET%' --sshfp -a '-oUserKnownHostsFile=%USERDIR%/%USER%/ssh/known_hosts'"
ca_certs = None
js_init = ""
keyfile = "keyfile.pem"
log_file_prefix = "/opt/gateone/logs/webserver.log"
api_keys = "M2RlNGQyMzI4ZjE0NGE3Mzg4OTg3NDkzNDAzMDM4OGY0N:YjgzZmQyZGNjYjY3NDkwYzhmZDM3NDYzZDRjMmQyODM2M, MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M:secret"

@liftoff
Copy link
Owner

liftoff commented Sep 3, 2012

OK, I believe your problem is due to a mixture of an old version of gateone.js and a current version of Gate One (server). Are you serving up a copy of gateone.js from your web server that's different from the one that comes with Gate One?

I ask because that "/style?theme=black&container=gateone&prefix=go_" URL is something Gate One doesn't use anymore. The Github code loads CSS over the WebSocket and has been doing so for quite some time now (a few months?).

@maxking
Copy link

maxking commented Sep 3, 2012

I did switch between the 1.0.2 release and the latest repo for i was facing problems with both. I will try to reinstall completely again.

Also i wanted to ask that is it possible to somehow use the GatOne.init() in backend so that i can hide password in autoConnectURL?

@liftoff
Copy link
Owner

liftoff commented Sep 3, 2012

The autoConnectURL is really meant for things where revealing the password doesn't matter or situations where you've setup pre-shared SSH keys. The alternative is to write your own wrapper around the SSH command that doesn't take any input at all but instead just connects automatically to the host that you want. Something as simple as a shell script would do the trick.

@maxking
Copy link

maxking commented Sep 3, 2012

Thanks for your sugession. Is there any example for such kind of wrapper in documentation?

@liftoff
Copy link
Owner

liftoff commented Sep 4, 2012

Well, yes... ssh_connect.py itself is just a wrapper around the SSH command :)

It creates a tiny shell script and executes that... If you take a look at the top of the script you'll see the template it uses. It should be pretty easy to copy the template to have it execute a hard-coded 'ssh whatever' command.

@maxking
Copy link

maxking commented Sep 4, 2012

I recently switched to archlinux and python3.2.3.
There is a syntax error in termio.py line 113. How to fix that?

@liftoff
Copy link
Owner

liftoff commented Sep 5, 2012

You should've opened a new issue for the Python 3 thing... Having said that I'll be pushing a commit shortly that fixes some Python 3-specific bugs.

For reference, if you were running Gate One on Python 2.X and now you're running it on Python 3.X you need to run Gate One's setup.py using python 3... It will overwrite all the .py files and update them to work with Python 3.

...but before you do that give me a few minutes to write some proper commit messages and push out the update :)

@irraju
Copy link

irraju commented Jun 3, 2013

Hi @liftoff,
Thanks for your detailed insights into how authentication works for gateone. I am looking for a way I should be able to authenticate user using SSH private key. I am using gateone on javascript and I have the user name and his private key and I want to authenticate user based on these two parameters. If there is a way available already?

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

No branches or pull requests

4 participants