Skip to content

Commit e8983ca

Browse files
committed
Fix deployment (in and out of docker)
Don't rebuild file paths all over the place Move docker-compose.yml to readme
1 parent 3c498c0 commit e8983ca

10 files changed

+107
-111
lines changed

.dockerignore

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
.dockerignore
2-
*.yml
2+
.gitignore
3+
Dockerfile
4+
35
.git
46
target
7+
data
8+
9+
*.code-workspace
10+
*.yml

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
*.raw
44
*.storage
55
*.cache
6+
*.code-workspace
7+
/data
8+
*.yml
69

710
# Cargo
811
/target

Dockerfile

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ WORKDIR /enokey
2626
RUN mkdir keyfiles
2727

2828
RUN apt-get update \
29-
&& apt-get install -y libssl1.1 ca-certificates --no-install-recommends \
29+
&& apt-get install -y libssl1.1 ca-certificates openssh-client --no-install-recommends \
3030
&& rm -rf /var/lib/apt/lists/*
3131

3232
COPY --from=build /service/enokey/target/debug/enokey .
@@ -36,4 +36,7 @@ COPY ./Rocket.toml ./Rocket.toml
3636
ENV ROCKET_ENV production
3737
ENV ROCKET_TEMPLATE_DIR static
3838

39-
ENTRYPOINT "./enokey"
39+
RUN adduser --disabled-password --gecos '' enokey
40+
RUN chown -R enokey .
41+
USER enokey
42+
ENTRYPOINT "./enokey"

README.md

+23-54
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,28 @@
44
[![](https://tokei.rs/b1/github/ENOFLAG/ENOKEY)](https://github.com/ENOFLAG/ENOKEY)
55

66

7-
This is the ENOKEY project for handling ssh keys. It contains the following tools:
7+
# Docker
88

9-
* enokey - Collect, Generate and Distribute an authorized_keys from user input over a webservice
10-
11-
12-
# Usage
13-
```
14-
$ ./enokey --help
15-
Usage: target/debug/enokey [options]
16-
17-
Options:
18-
-s, --storage ENOKEYS.storage
19-
The output ENOKEYS storage file
20-
-o, --authorized_keys authorized_keys
21-
The output authorized_keys file
22-
-n, --dry-run Do not write to cfg file
23-
-h, --help Print this help menu
24-
```
25-
26-
# Configuration Files
27-
28-
The configuration files declare which keys are to be included into the output file of authorized keys. There are three commands supported to include keys:
29-
30-
* provider:username - use the SSH keys stored on a well-known provider (currently GitHub and Gitlab)
31-
* pubkey:actual-key - just include this key, formatted like usual in authorized key files
32-
* include:file - include another config file
33-
34-
An example is given in [example-config](example-config):
35-
36-
```
37-
# Comments and empty lines are ignored
38-
39-
# Use the scheme <provier>:<username> to scrape the keys there
40-
github:torvalds
41-
gitlab:stallman
42-
43-
# Just add a single key
44-
pubkey:ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyiWjcfZ2QN2aigzjvvAMmLJ70lXbN92IGyuY3tQOrA162Jtn6OSDIUNcR3q8as6LrGlX2LJZAygndB59Mb12Zddv2nB/UuanD3x1R47fMA2iliMjanQSjDbtEgtDi6u/cArvb1PA4P9FUjxUx7RdNKd4RuYrFyOVMmPpbqD7x5QBHZT7y43mrHCYAoYEoOZdVrXcMVxnit2iN9oA3f+h5GmVRgciIXxgqBbdvRmADBrR9jkeQGFPOVdRfVGLxpFMeM+abm3+JmJIMxneiLcO2hxx+47MvMuALrLzoSztkks+HeiRkiv1bXOuXdUMFcrHNuwaJ/f5lqJtp8fdJ1+riQ== randomuser@machine
45-
46-
# Include a config file
47-
#include:another-config
48-
```
49-
50-
This is converted to the following:
51-
52-
```
53-
###
54-
# Config: example-config
55-
###
56-
# Keys from torvalds@github
57-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoQ9S7V+CufAgwoehnf2TqsJ9LTsu8pUA3FgpS2mdVwcMcTs++8P5sQcXHLtDmNLpWN4k7NQgxaY1oXy5e25x/4VhXaJXWEt3luSw+Phv/PB2+aGLvqCUirsLTAD2r7ieMhd/pcVf/HlhNUQgnO1mupdbDyqZoGD/uCcJiYav8i/V7nJWJouHA8yq31XS2yqXp9m3VC7UZZHzUsVJA9Us5YqF0hKYeaGruIHR2bwoDF9ZFMss5t6/pzxMljU/ccYwvvRDdI7WX4o4+zLuZ6RWvsU6LGbbb0pQdB72tlV41fSefwFsk4JRdKbyV3Xjf25pV4IXOTcqhy+4JTB/jXxrF torvalds@github (1)
58-
# Keys from stallman@gitlab
59-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCfu9PhGt4+RcGdxpHnbu129OBNhXZhUEpVVWTXeOwq68+CgdE25hjW3qyJkNDe/3uno9ogCg/FXa083r6bQt5YJU65o22yYBFXe+m1OcT4Uw56nkcT9hjJqJxHg1+DWKlheNhth5VQOVueyN8SPTKU6ezelcpWiOXfRBu5DOhGKkooT98f4HiujmrSkCD/1WIjAA4m0rBYF8PmXLW0qFiiw4mPxAAVXRu+lF6tPTqT9gSwUTgKcJ/LTd79caU0H0jsqsF9S/+s7/dMqR3TRGVnAUUQJlKyizA9mg2mJ91bBVGVSE/Aiyo3788vZekBqM7mWI74ZgePwf+EgT7yRlf1 stallman@gitlab (1)
60-
# Keys from pubkey
61-
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyiWjcfZ2QN2aigzjvvAMmLJ70lXbN92IGyuY3tQOrA162Jtn6OSDIUNcR3q8as6LrGlX2LJZAygndB59Mb12Zddv2nB/UuanD3x1R47fMA2iliMjanQSjDbtEgtDi6u/cArvb1PA4P9FUjxUx7RdNKd4RuYrFyOVMmPpbqD7x5QBHZT7y43mrHCYAoYEoOZdVrXcMVxnit2iN9oA3f+h5GmVRgciIXxgqBbdvRmADBrR9jkeQGFPOVdRfVGLxpFMeM+abm3+JmJIMxneiLcO2hxx+47MvMuALrLzoSztkks+HeiRkiv1bXOuXdUMFcrHNuwaJ/f5lqJtp8fdJ1+riQ== randomuser@machine pubkey (1)
9+
ENOKEY is configured with environment variables. Here is an example using docker-compose.yml:
6210
```
11+
version: '3'
12+
13+
services:
14+
enokey:
15+
build: .
16+
volumes:
17+
- ./data:/enokey/data
18+
restart: on-failure
19+
ports:
20+
- "80:8000"
21+
environment:
22+
- ROCKET_PORT=8000
23+
- ROCKET_ENV=production
24+
- ROCKET_LOG=normal
25+
- ROCKET_SECRET_KEY=whs/vijJnEoWN9Xgf25oJDn2yUtvsNuhm0eMNxZe6CI=
26+
27+
- PSK_ADMIN=HIGHLYSECRET
28+
29+
- PSK_USER=NOTSOSECRET
30+
- RUST_BACKTRACE=1
31+
```

docker-compose.yml

-19
This file was deleted.

src/deploy.rs

+7-15
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
1-
use std::net::TcpStream;
2-
use ssh2::Session;
3-
use std::path::Path;
4-
use std::io::Write;
5-
use std::io::Read;
6-
use std::fs::File;
1+
use std::process::Command;
72

83
use Destination;
94
use EnokeysError;
105

116

127
pub fn deploy(destinations: &[Destination]) -> Result<(), EnokeysError> {
138
for destination in destinations {
14-
let file_name = format!("keyfiles/{}.authorized_keys",destination.destination_name);
15-
let mut content = vec!();
16-
File::open(file_name)?.read_to_end(&mut content)?;
17-
let tcp = TcpStream::connect(&destination.address)?;
18-
let mut sess = Session::new().unwrap();
19-
sess.handshake(&tcp)?;
20-
sess.userauth_agent(&destination.userauth_agent)?;
21-
let mut remote_file = sess.scp_send(Path::new("~/.ssh/authorized_keys"), 0o644, content.len() as u64, None)?;
22-
remote_file.write_all(&content)?;
9+
Command::new("scp")
10+
.args(&["-i", "./data/id_ed25519",
11+
"-P", &format!("{}", destination.port),
12+
"-o", "StrictHostKeyChecking=no",
13+
&destination.authorized_keys_file_name.to_str().unwrap().to_string(), &format!("{}@{}:/home/{}/.ssh/authorized_keys", &destination.userauth_agent, &destination.address, &destination.userauth_agent)])
14+
.status()?;
2315
}
2416
Ok(())
2517
}

src/error.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pub enum EnokeysError {
33
IOError(std::io::Error),
44
InvalidEnvironmentError,
5+
InvalidIntegerError,
56
ReqwestError(reqwest::Error),
67
Ssh2Error(ssh2::Error),
78
InvalidData(String),
@@ -26,3 +27,9 @@ impl From<std::io::Error> for EnokeysError {
2627
EnokeysError::IOError(error)
2728
}
2829
}
30+
31+
impl From<std::num::ParseIntError> for EnokeysError {
32+
fn from(_error: std::num::ParseIntError) -> Self {
33+
EnokeysError::InvalidIntegerError
34+
}
35+
}

src/main.rs

+31-10
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ lazy_static! {
5252
pub struct Destination {
5353
address: String,
5454
userauth_agent: String,
55-
destination_name: String
55+
destination_name: String,
56+
authorized_keys_file_name: PathBuf,
57+
raw_storage_file_name: PathBuf,
58+
providers_storage_file_name: PathBuf,
59+
port: u16
5660
}
5761

5862
pub struct Context {
@@ -170,10 +174,10 @@ fn deploy_get() -> Template {
170174
let config = &*CONFIG.lock().unwrap();
171175
storage::generate_authorized_key_files(&config.admin_destinations).unwrap();
172176
let mut context = HashMap::new();
173-
let dest_names: Vec<String> = config.admin_destinations
177+
let destinations: Vec<String> = config.admin_destinations
174178
.iter()
175-
.map(|a| a.destination_name.to_string()).collect();
176-
context.insert("dest_names", dest_names);
179+
.map(|a| a.authorized_keys_file_name.to_str().unwrap().to_string()).collect();
180+
context.insert("destinations", destinations);
177181
Template::render("deploy", &context)
178182
}
179183

@@ -275,9 +279,9 @@ fn main() {
275279
println!("Warning: PSK_ADMIN not set. {:?}",e);
276280
"default".to_string()
277281
});
278-
}
279-
280282

283+
storage::load_deploy_keypair().unwrap();
284+
}
281285

282286
rocket::ignite()
283287
.mount("/", routes![static_files, index_post, index_get, deploy_get, deploy_post, favicon, key_files])
@@ -286,7 +290,10 @@ fn main() {
286290
}
287291

288292
fn parse_destinations(input: &str) -> Result<Vec<Destination>, EnokeysError> {
289-
let entries : Vec<&str> = input.split(',').collect();
293+
if input == "" {
294+
return Ok(vec!())
295+
}
296+
let entries : Vec<&str> = input.split(",").collect();
290297
println!("{:?}",&entries);
291298
let mut destinations = vec!();
292299
for entry in entries {
@@ -295,11 +302,25 @@ fn parse_destinations(input: &str) -> Result<Vec<Destination>, EnokeysError> {
295302
2 => (split[0], split[1]),
296303
_ => return Err(EnokeysError::InvalidEnvironmentError)
297304
};
298-
destinations.push(Destination{
305+
let port = parse_port(address)?;
306+
let address = address.split(":").collect::<Vec<&str>>()[0];
307+
destinations.push(Destination {
299308
address: address.to_string(),
300309
userauth_agent: userauth_agent.to_string(),
301-
destination_name: entry.to_string()
310+
destination_name: format!("{}@{}:{}", &userauth_agent, &address, port),
311+
authorized_keys_file_name: PathBuf::from(format!("./keyfiles/{}@{}_{}.authorized_keys", &userauth_agent, &address, port)),
312+
raw_storage_file_name: PathBuf::from(format!("./keyfiles/{}@{}_{}.authorized_keys.raw", &userauth_agent, &address, port)),
313+
providers_storage_file_name: PathBuf::from(format!("./keyfiles/{}@{}_{}.authorized_keys.providers", &userauth_agent, &address, port)),
314+
port: port
302315
})
303316
}
304317
Ok(destinations)
305-
}
318+
}
319+
320+
fn parse_port(address: &str) -> Result<u16, EnokeysError> {
321+
let split = address.split(":").collect::<Vec<&str>>();
322+
if split.len() == 1 {
323+
return Ok(22);
324+
}
325+
return Ok(split[split.len()-1].parse::<u16>()?);
326+
}

src/storage.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn handle_raw_submission(name: &str, pub_key: &str, destinations: &[Destinat
1919
.write(true)
2020
.create(true)
2121
.append(true)
22-
.open(format!("./data/{}.storage.raw", destination.destination_name))?;
22+
.open(&destination.raw_storage_file_name)?;
2323
writeln!(&raw_storage_file, "{} {}@raw", &pub_key, &name)?;
2424
}
2525
Ok(())
@@ -36,7 +36,7 @@ pub fn handle_submission(provider: &str, user_name: &str, name: &str, destinatio
3636
.write(true)
3737
.create(true)
3838
.append(true)
39-
.open(format!("./data/{}.storage", &destination.destination_name))?;
39+
.open(&destination.providers_storage_file_name)?;
4040
let line = format!("# {} \n{}:{}\n", &name, provider, &user_name);
4141
println!("Adding entry:\n{}", &line);
4242
write!(storage_file, "{}", &line)?;
@@ -46,20 +46,27 @@ pub fn handle_submission(provider: &str, user_name: &str, name: &str, destinatio
4646

4747
pub fn generate_authorized_key_files(destinations: &[Destination]) -> Result<(), EnokeysError> {
4848
for destination in destinations {
49-
let mut authorized_keys_file = File::create(format!("./keyfiles/{}.authorized_keys", &destination.destination_name))?;
50-
let mut authorized_keys_file_content = String::new();
49+
let mut authorized_keys_file = File::create(&destination.authorized_keys_file_name)?;
5150
let mut storage_file = OpenOptions::new()
5251
.read(true)
5352
.write(true)
5453
.create(true)
55-
.open(format!("./data/{}.storage", destination.destination_name))?;
54+
.open(&destination.providers_storage_file_name)?;
5655
let mut storage_file_content = String::new();
5756

57+
// append deploy key
58+
let mut deploy_key = String::new();
59+
// TODO: User-configable ssh key
60+
if let Ok(mut deploy_key_file) = File::open("./data/id_ed25519.pub") {
61+
deploy_key_file.read_to_string(&mut deploy_key)?;
62+
write!(authorized_keys_file, "{}", &deploy_key)?
63+
}
64+
65+
5866
// append raw keys
5967
let mut raw_keys = String::new();
60-
if let Ok(mut raw_keys_file) = File::open(format!("./data/{}.storage.raw", destination.destination_name)) {
68+
if let Ok(mut raw_keys_file) = File::open(&destination.raw_storage_file_name) {
6169
raw_keys_file.read_to_string(&mut raw_keys)?;
62-
authorized_keys_file_content.push_str(&raw_keys);
6370
write!(authorized_keys_file, "{}", &raw_keys)?
6471
}
6572

@@ -76,7 +83,6 @@ pub fn generate_authorized_key_files(destinations: &[Destination]) -> Result<(),
7683
Some(ref comment) => {
7784
let comment = USERNAME_REGEX.replace_all(&comment, " ");
7885
let line = format!("{} {} {}\n", key.keytype(), base64::encode(&key.data()), &comment[0..min(comment.len(), 100)]);
79-
authorized_keys_file_content.push_str(&line);
8086
write!(authorized_keys_file, "{}", &line)?
8187
},
8288
None => writeln!(authorized_keys_file, "{} {}", key.keytype(), base64::encode(&key.data()))?
@@ -89,3 +95,11 @@ pub fn generate_authorized_key_files(destinations: &[Destination]) -> Result<(),
8995
}
9096
Ok(())
9197
}
98+
99+
pub fn load_deploy_keypair() -> Result<(), EnokeysError> {
100+
// TODO: User-configable ssh key
101+
match File::open("./data/id_ed25519") {
102+
Ok(_) => Ok(()),
103+
Err(e) => Err(EnokeysError::IOError(e))
104+
}
105+
}

static/deploy.html.hbs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
<nav class="navbar navbar-expand-lg navbar-light bg-light">
1515
<a class="navbar-brand" href="/">ENOKEYS - SSH PublicKey Self-Service Center</a>
1616
</nav>
17-
{{#each dest_names}}
18-
<div><a href="../keyfiles/{{this}}.authorized_keys">{{this}}.authorized_keys</a></div>
17+
{{#each destinations}}
18+
<div><a href="../{{this}}">{{this}}</a></div>
1919
{{/each}}
2020
<div class="container">
2121
<div class="row">

0 commit comments

Comments
 (0)