Skip to content

Commit 3b0ed12

Browse files
committed
avoid ssh-import-ids, cache keys by our own
it can be flaky, for example non-existent user or users without keys can silently result into incomplete authorized_keys file: https://bugs.launchpad.net/ssh-import-id/+bug/2031332
1 parent eeb88df commit 3b0ed12

10 files changed

Lines changed: 101 additions & 11 deletions

File tree

tf/acme-responder/userdata.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
local cloud_config = (import '../cloudconfig.base.libsonnet') + {
2-
runcmd: [
2+
runcmd+: [
33
['networkctl', 'reload'],
44
],
55
write_files: [

tf/bastion/.terraform.lock.hcl

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tf/bastion/aws.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ provider "aws" {
1010
}
1111

1212
data "aws_caller_identity" "current" {}
13+
data "aws_region" "current" {}

tf/bastion/bastion-onpremises.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ resource "aws_instance" "bastion-onpremises" {
1414
tags = {
1515
Name = "bastion-onpremises"
1616
}
17+
depends_on = [null_resource.s3-authorized-keys]
1718
lifecycle {
1819
ignore_changes = [ami]
1920
}

tf/bastion/bastion.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ resource "aws_instance" "bastion" {
5151
tags = {
5252
Name = "bastion"
5353
}
54+
depends_on = [null_resource.s3-authorized-keys]
5455
lifecycle {
5556
ignore_changes = [ami]
5657
}

tf/bastion/ssh_keys.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
require 'bundler/inline'
2+
gemfile do
3+
source 'https://rubygems.org'
4+
gem 'aws-sdk-s3'
5+
gem 'rexml'
6+
end
7+
require 'open-uri'
8+
require 'json'
9+
require 'logger'
10+
11+
ssh_import_id = JSON.parse(File.read(ENV['SSH_IMPORT_ID_FILE']))
12+
13+
all_keys = []
14+
ssh_import_id.each do |x|
15+
next unless x.start_with?('gh:')
16+
login = x.sub(/^gh:/,'')
17+
18+
url = "https://api.github.com/users/#{login}/keys"
19+
p url
20+
keys = JSON.parse(URI.open(url, "r", &:read))
21+
22+
nonrsa = keys.map { _1.fetch('key') }.any? { _1.start_with?('ssh-ed25519') || _1.start_with?('ecdsa-') }
23+
24+
keys.each do |key|
25+
kty = {
26+
'ssh-rsa' => :rsa,
27+
'ecdsa-sha2-nistp256' => :ecdsa,
28+
'ecdsa-sha2-nistp384' => :ecdsa,
29+
'ecdsa-sha2-nistp521' => :ecdsa,
30+
'ssh-ed25519' => :ed25519,
31+
}[key.fetch('key').split(' ', 2)[0]]
32+
next unless kty
33+
next if nonrsa && kty == :rsa
34+
35+
all_keys << "#{key.fetch('key')} #{login}@github/#{key.fetch('id')}"
36+
end
37+
end
38+
39+
Aws::S3::Client.new(logger: Logger.new($stdout))
40+
.put_object(
41+
bucket: ENV.fetch('S3_BUCKET'),
42+
key: ENV.fetch('S3_KEY'),
43+
content_type: 'text/plain',
44+
body: all_keys.join("\n") + "\n",
45+
)

tf/bastion/ssh_keys.tf

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
data "aws_s3_bucket" "dot-net" {
2+
bucket = "rubykaigi-dot-net"
3+
}
4+
5+
resource "null_resource" "s3-authorized-keys" {
6+
triggers = {
7+
hash1 = sha512(file("${path.module}/ssh_keys.rb"))
8+
hash2 = sha512(file("${path.module}/../../data/ssh_import_ids.json"))
9+
}
10+
provisioner "local-exec" {
11+
command = "ruby ssh_keys.rb"
12+
environment = {
13+
SSH_IMPORT_ID_FILE = "${path.module}/../../data/ssh_import_ids.json"
14+
S3_BUCKET = data.aws_s3_bucket.dot-net.bucket
15+
S3_KEY = "authorized_keys"
16+
AWS_REGION = data.aws_region.current.name
17+
}
18+
}
19+
}
20+
21+

tf/cloudconfig.base.libsonnet

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55
primary_group: 'rk',
66
uid: 3333,
77
groups: 'adm,sudo,plugdev,netdev,lxd',
8-
ssh_import_id: import '../data/ssh_import_ids.json',
8+
ssh_authorized_keys: ['# dummy comment'],
99
sudo: 'ALL=(ALL) NOPASSWD: ALL',
1010
shell: '/bin/bash',
1111
},
1212
],
13+
14+
runcmd_get_authorized_keys:: [
15+
['curl', '-o', '/home/rk/.ssh/authorized_keys.rknet', 'https://rubykaigi.net/authorized_keys'], // //tf/bastion/ssh_keys.tf
16+
['bash', '-c', 'cat /home/rk/.ssh/authorized_keys.rknet | tee -a /home/rk/.ssh/authorized_keys'],
17+
['rm', '/home/rk/.ssh/authorized_keys.rknet'],
18+
['ls', '-la', '/home/rk/.ssh'],
19+
],
20+
21+
runcmd: [] +
22+
$.runcmd_get_authorized_keys,
1323
}

tf/cloudprober/cloudconfig.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
],
1919

20-
runcmd: [
20+
runcmd+: [
2121
['systemctl', 'daemon-reload'],
2222
['systemctl', 'enable', '--now', 'cloudprober.service'],
2323
],

tf/tftp/default.jsonnet

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ local user_data = (import '../cloudconfig.base.libsonnet') + {
66
'iperf3',
77
'mtr',
88
],
9-
users+: [
10-
super.users[0] {
11-
// FIXME: ssh_import_id is flaky...
12-
ssh_authorized_keys: [
13-
'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACG1cKNR8SS4Dkm2wcia74RRmy9d7h62114MQd0H9zb1+1LxVa55Qqd8O232BH1i/fF/1o+eE3L5U7RCR8KUCuAXgFrF429BETaiiBnSErv5yrHJS5RTTjEhA1d9Ygk0o3Und6+90waBXAk2oPVP+OBNtYq1CraZQsXuqvlUtMrBnSTsQ== sorah-mulberry-ecdsa',
14-
],
15-
},
16-
],
179
};
1810
local autoinstall = {
1911
autoinstall: {

0 commit comments

Comments
 (0)